From: Joerg L. <jo...@us...> - 2004-01-11 13:14:08
|
Hi, as mentioned by Magnus in a previous mail to this list, the current CVS version of PyX contains some important changes in the decorator classes which have been introduced during the rewrite of the attribute system. Since we, André, Michael and myself, are not really sure whether these changes are really heading in the correct direction, I would like to stimulate some discussions on this list. Let us start with an important type of attributes for drawing paths in PyX, the so-called decorators. These allow to specify - the kind of drawing (stroked, filled, or both) and - modifications applied to the paths (at the moment only arrows, but many more are conceivable) An important feature of the currently implemented decorators is that one can modify their meaning by using constructs like - canvas.stroked(color.rgb.blue) - canvas.filled(somepattern) - canvas.earrow.normal(canvas.stroked(color.rgb.red), canvas.filled(color.rgb.blue)) We think that this gives a flexible, yet compact syntax to specify the way paths are drawn. Note also that it makes perfect sense to use decorators without arguments (e.g. canvas.stroked()) The arrow example above shows another feature of the attribute system in PyX: each attribute "class" contains more specialised versions as "Python attributes"/member variables: - canvas.linewidth("0.2 cm") == canvas.linewidth.normal - color.rgb(1, 0, 0) == color.rgb.red - canvas.earrow.normal = earrow(size="4 v pt") This way, a structure of the attribute tree is introduced automatically. Note furthermore that - in contrast to the situation for the decorators discussed above - using canvas.linewidth without argument does not make any sense and does not work. Similarly, further specialising canvas.linewidth.normal is not possible. So everything is nice and consistent? Not completely, as becomes clear if one looks at the two arrow examples: canvas.earrow.normal is already a working decorator but it can still be changed as shown in the third line of the upper examples: canvas.earrow.normal(...). Furthermore, if canvas.earrow.normal works directly, why is this not the case for canvas.stroked, i.e., why is it necessary to write canvas.stroked()? Another point is that the form of the arrow heads can be changed, for instance the tip angle: - canvas.earrow(size="4 v pt", angle=30) Unfortunately, this cannot be easily combined with the syntax described above, i.e. it is impossible to use: - canvas.earrow.normal(canvas.stroked(color.rgb.red), canvas.filled(color.rgb.blue), angle=30) So what do we do? Let us first look at the... Old attribute system (PyX 0.4.1 and below) ------------------------------------------ You had to instantiate canvas.stroked, but it canvas.earrow.normal was already an instance. However, there was a __call__ method of the arrow class, which acted as a factory for new arrow instances. Allowed constructs were: - canvas.stroked() - canvas.stroked(color.rgb.red) - canvas.earrow.normal - canvas.earrow.normal(color.rgb.red) - canvas.earrow(size="4 v pt", angle=30, styles=color.rgb.red) - canvas.linewidth("0.2 cm") - canvas.linewidth.normal Pros: - Relatively consistent in the sense that attribute "classes" (canvas.stroked, canvas.linewidth, ...) have to be instantiated Cons: - From the user perspective it does not make too much sense to instantiate for instance canvas.stroked. - canvas.earrow.normal is an exception since it can be used both with and without "calling" - Is using __call__ as a factory function really a good idea? Attribute system in CVS ----------------------- One goal of the rewrite of the attribute system was to move the path attributes out of the canvas module into two new modules for decorators and styles. Furthermore, there was some unhappiness about the use of __call__ methods and we thus tried to not use them. Therefore, the following solution has been implemented: The decorators have been moved to the deco module and always have to be instantiated. Allowed constructs are: - deco.stroked() - deco.stroked(color.rgb.red) - deco.earrow.normal() - deco.earrow.normal(color.rgb.red) - deco.earrow.normal(color.rgb.red, angle=30) - deco.earrow.normal([color.rgb.red, deco.stroked(color.rgb.blue)], angle=30) - deco.earrow(size="4 v pt", angle=30, attrs=color.rgb.red) Styles now live in the style module, but behave otherwise as before: - style.linewidth("0.2 cm") - style.linewidth.normal Pros: - all decorators behave consistently in the sense that the have to be instantiated before their use - no more use of __call__ (?) Cons: - From the user perspective it does not make too much sense to instantiate deco.stroked and deco.earrow.normal - deco.earrow.normal() vs. style.linewidth.normal - deco.earrow.normal(color.rgb.red, deco.stroked(color.rgb.blue)) does not work Alternative system ------------------ A possible alternative solution would consists of decorators, which are already instantiated. Calling them would allow to change their look. Allowed constructs would be: - deco.stroked - deco.stroked() - deco.stroked(color.rgb.red) - deco.earrow.normal - deco.earrow.normal() - deco.earrow.normal(color.rgb.red) However, this still leaves unsolved the problem with syntax of the arrows: - Is deco.earrow also an instance or a class? - Should deco.earrow.normal(color.rgb.red, deco.stroked(color.rgb.blue)) and thus deco.earrow.normal(color.rgb.red, angle=30) not? - Do deco.earrow and deco.earrow.normal behave identically when being called? - Or should we do something completely different for the arrows? I really hope that everybody is still following me after these lengthy discussion of the attribute syntax of PyX, but I'm sure the points raised are really worth some thoughts. So now it's time to bring forward remarks, helpful comments, suggestions, etc. Jörg |
From: Magnus L. H. <ma...@he...> - 2004-01-11 13:50:58
|
Joerg Lehmann <jo...@us...>: > [snip] > Let us start with an important type of attributes for drawing paths in PyX, > the so-called decorators. These allow to specify > > - the kind of drawing (stroked, filled, or both) and > - modifications applied to the paths (at the moment only arrows, but many > more are conceivable) > > An important feature of the currently implemented decorators is that one can > modify their meaning by using constructs like > > - canvas.stroked(color.rgb.blue) > - canvas.filled(somepattern) > - canvas.earrow.normal(canvas.stroked(color.rgb.red), > canvas.filled(color.rgb.blue)) Is it possible to modify parts of the decorators? If not, could that be a possible extension while you're restructuring this anyway? The reason for the suggestion is that I'd like to be able to specify different decorators for the line and the arrowhead of an arrow, for example. [snip] > Cons: > - From the user perspective it does not make too much sense > to instantiate deco.stroked and deco.earrow.normal > - deco.earrow.normal() vs. style.linewidth.normal > - deco.earrow.normal(color.rgb.red, deco.stroked(color.rgb.blue)) > does not work Also, it seems odd to have to instantiate unspecialized decorations but not styles... > A possible alternative solution would consists of decorators, which > are already instantiated. Calling them would allow to change their > look. This would at least make the mechanism consistent across the two modules. I think this seems quite all right -- but you could, I guess, separate things by, for example, using deco.stroked.default instead of deco.stroked Then deco.stroked could still be a class, and deco.stroked.default could be the default instantiation. Similarly: deco.earrow.normal.default would be the default instantiation of the normal arrow. A bit awkward, though, especially in this arrow case. Or... If you don't like the use of __call__, you could use named method... E.g. deco.stroked vs. deco.stroked.with(color.rgb.red) Then it's quite clear that deco.stroked is an instance. It seems that 'with' could be an appropriate name for most decorators and styles, and it could simply return a clone which is modified according to the arguments. > - Should deco.earrow.normal(color.rgb.red, deco.stroked(color.rgb.blue)) and > thus deco.earrow.normal(color.rgb.red, angle=30) not? Not sure if I understand the question... :} > - Do deco.earrow and deco.earrow.normal behave identically when > being called? That would seem to be the logical conclusion (even with an explicitly named cloning/specialization method). > - Or should we do something completely different for the arrows? I don't see why arrows are so different? You could (in general) use positional arguments to supply other modifiers/decorators/styles and keyword arguments to set attributes (or something equivalent). That could go for all the classes, no? As for my previous note about arrowheads styled separately... One could perhaps do something like: One could, perhaps, use something like: canvas.stroke(p, style.linestyle.dashed, deco.earrow.normal(style.linestyle.solid)) or something (that is, with the solid part only applying to the arrowhead)? Possibly with a different syntax for the specialization, though (such as, for example, the 'with' method or the like :). Or maybe this stuff is already possible? Just some thoughts... -- Magnus Lie Hetland "The mind is not a vessel to be filled, http://hetland.org but a fire to be lighted." [Plutarch] |
From: Joerg L. <jo...@us...> - 2004-01-12 20:40:11
|
On 11.01.04, Magnus Lie Hetland wrote: [snip] > > Cons: > > - From the user perspective it does not make too much sense > > to instantiate deco.stroked and deco.earrow.normal > > - deco.earrow.normal() vs. style.linewidth.normal > > - deco.earrow.normal(color.rgb.red, deco.stroked(color.rgb.blue)) > > does not work > > Also, it seems odd to have to instantiate unspecialized decorations > but not styles... Agreed. > > A possible alternative solution would consists of decorators, which > > are already instantiated. Calling them would allow to change their > > look. > > This would at least make the mechanism consistent across the two > modules. > > I think this seems quite all right -- but you could, I guess, separate > things by, for example, using > > deco.stroked.default > > instead of > > deco.stroked We also thought once about something similar but in the end we decided that this is too long to write. > Or... If you don't like the use of __call__, you could use named > method... E.g. > > deco.stroked > > vs. > > deco.stroked.with(color.rgb.red) > > Then it's quite clear that deco.stroked is an instance. It seems that > 'with' could be an appropriate name for most decorators and styles, > and it could simply return a clone which is modified according to the > arguments. Nice idea. > > - Should deco.earrow.normal(color.rgb.red, deco.stroked(color.rgb.blue)) and > > thus deco.earrow.normal(color.rgb.red, angle=30) not? > > Not sure if I understand the question... :} What question ;-) Seriously, what I meant was that formerly canvas.earrow.normal(color.rgb.red, deco.stroked(color.rgb.blue)) worked (because the __call__ method did expect a list of attributes), while now you have to write deco.earrow.normal(attrs=[color.rgb.red, deco.stroked(color.rgb.blue)) in order to allow other parameters like, e.g., angle. > > - Or should we do something completely different for the arrows? > > I don't see why arrows are so different? You could (in general) use > positional arguments to supply other modifiers/decorators/styles and > keyword arguments to set attributes (or something equivalent). That > could go for all the classes, no? See my point above. > As for my previous note about arrowheads styled separately... One > could perhaps do something like: > > One could, perhaps, use something like: > > canvas.stroke(p, style.linestyle.dashed, > deco.earrow.normal(style.linestyle.solid)) > > or something (that is, with the solid part only applying to the > arrowhead)? Possibly with a different syntax for the specialization, > though (such as, for example, the 'with' method or the like :). Or > maybe this stuff is already possible? As you already figured out this is possible - that was one of the goals when the whole decorator syntax was conceived. > Just some thoughts... Your feedback is really appreciated! Jörg -- JOERG LEHMANN | PyX - High quality PostScript figures with Python & TeX jo...@lu... | Visit http://pyx.sourceforge.net/ |
From: Magnus L. H. <ma...@he...> - 2004-01-12 21:38:28
|
Joerg Lehmann <jo...@us...>: > [snip] > in order to allow other parameters like, e.g., angle. Why not use *args and **kwds here? You could, for example, say that arguments such as angle _must_ be specified as keyword arguments. Then all positional arguments will behave like before... Or? I don't think requiring angle and the like to be keyword arguments is a tough restriction -- it will probably just make the code (that is, the code using pyx) easier to read. E.g. deco.earrow.normal(style.linestyle.solid, color.rgb.red, angle=3D20) vs. deco.earrow.normal(attrs=3D[style.linestyle.solid, color.rgb.red], 20) Given that the attribute arguments are as prominent as they are in the API in general, it seems fair to give them some privileges here too... Also, using different line styles and colours seems to be a more common thing to do than changin (e.g.) the angle of the arrowhead. (Just guessing here, though.) But then again, I don't know the API that well -- this is just stuff off the top of my head :) > J=F6rg --=20 Magnus Lie Hetland "The mind is not a vessel to be filled, http://hetland.org but a fire to be lighted." [Plutarch] |
From: Andre W. <wo...@us...> - 2004-01-13 06:48:14
|
Hi, On 12.01.04, Magnus Lie Hetland wrote: > > in order to allow other parameters like, e.g., angle. > > Why not use *args and **kwds here? You could, for example, say that > arguments such as angle _must_ be specified as keyword arguments. Then > all positional arguments will behave like before... Or? This might work out for those decorators, which do have only one set of attributes. But consider a decorator, which fills some arbitrary path and writes a label into the filled area. Hence there are two distinct fill operations in this example. Now you would need to use keyword arguments taking lists of attributes with no excuse, wouldn't you? (In the graph module, e.g. the axis painters and graph styles, those constructs are quite common.) Another possibility would be, that (only) there you could have two different with-methods: fillandlabel.withfillstyles(color.rgb.red).withlabelstyles(color.rgb.green) Hmmm? I don't really like it. Additionally, I'm against private *args and **kwargs handling (and mixing them both in one method at all). We could do so, but this would break pythons default behaviour. I guess, that we can quickly reach the situation, where we have to do our own argument handling everywhere around. Or we could get into some state, where the argument handling is inconsistent somewhere else. Imagine you write a own small function taking attributes and other parameters. Wouldn't it be likely, that you do not use some PyX internally created way of handling those function arguments? André -- by _ _ _ Dr. André Wobst / \ \ / ) wo...@us..., http://www.wobsta.de/ / _ \ \/\/ / PyX - High quality PostScript figures with Python & TeX (_/ \_)_/\_/ visit http://pyx.sourceforge.net/ |
From: Magnus L. H. <ma...@he...> - 2004-01-13 17:27:32
|
Andre Wobst <wo...@us...>: > > Hi, >=20 > On 12.01.04, Magnus Lie Hetland wrote: > > > in order to allow other parameters like, e.g., angle. > >=20 > > Why not use *args and **kwds here? You could, for example, say > > that arguments such as angle _must_ be specified as keyword > > arguments. Then all positional arguments will behave like > > before... Or? >=20 > This might work out for those decorators, which do have only one set > of attributes. Yes... I wasn't aware that they could have more. (As I said, I don't fully know the API yet -- sorry. :) How does it currently work with more than one set of attributes, then? Do you use several keyword arguments? > But consider a decorator, which fills some arbitrary > path and writes a label into the filled area. Hence there are two > distinct fill operations in this example. Now you would need to use > keyword arguments taking lists of attributes with no excuse, wouldn't > you? (In the graph module, e.g. the axis painters and graph styles, > those constructs are quite common.) In a quick glance through the docs I didn't find exactly what you're talking about. Are you saying that you have a list of lists, then? Like attrs=3D[[foo, bar, baz], # First set of attributes [fie, foe, fum]] # Second set of attributes This could, of course, be easily dealt with without the attrs kwd. argument too: somedecorator([foo, bar, baz], [fie, foe, fum]) But I guess you're probably talking about something else...? > Additionally, I'm against private *args and **kwargs handling (and > mixing them both in one method at all). Really? Why? (And, by the way, what do you mean by "private" in this context?) > We could do so, but this would break pythons default behaviour. Again I'm not sure I understand what you mean. (As for this sort of behaviour elsewhere, consider, for example, the range() function or the iter() function -- they are both based on this sort of thing, although implemented in C.) > I guess, that we can quickly reach the situation, where we have to > do our own argument handling everywhere around. Maybe. > Or we could get into some state, where the argument handling is > inconsistent somewhere else. I just thought it was more inconsistent to require the attrs=3D[] stuff in some classes and not in others. Confuses me, at least. > Imagine you write a own small function taking attributes and other > parameters. Wouldn't it be likely, that you do not use some PyX > internally created way of handling those function arguments? Sure. If you expect people to write small functions like these, that is, of course, a good point. I just thought the modifiers were part of the library -- and that one could, for example, use subclassing or something there. Anyway: I don't mind the use of attrs=3D[...] -- but shouldn't it be used everywhere, then? It's the inconsistency that trips me up. Then again, it may be convenient enough to drop the keyword argument where you don't need it to justify this minor inconsistency. I'm just commenting off the top of my head since you asked for feedback :) BTW: You *could* use a single positional argument for it, though, which could be a sequence... Instead of a keyword argument. (We considered that approach in Anygui at one time.) That is, something((some, attributes, here), some, other, arguments) A little less verbose, perhaps. And completely default argument handling. > Andr=E9 --=20 Magnus Lie Hetland "The mind is not a vessel to be filled, http://hetland.org but a fire to be lighted." [Plutarch] |
From: Joerg L. <jo...@us...> - 2004-01-13 18:12:56
|
Hi Magnus, thanks for "clearing my mind"! On 13.01.04, Magnus Lie Hetland wrote: [snip] > Anyway: I don't mind the use of attrs=[...] -- but shouldn't it be > used everywhere, then? It's the inconsistency that trips me up. You hit exactly the point. Inconsistency for the sake of some minor notational compactness is the wrong thing to do. I'm now really in favour of - c.draw(p, color.rgb.blue) - c.draw(p, [color.rgb.blue, deco.filled]) == c.draw(p, attrs=[color.rgb.blue, deco.filled]) - deco.stroked(color.rgb.blue) - deco.stroked([color.rgb.blue, style.linestyle.dashed]) == deco.stroked(attrs=[color.rgb.blue, style.linestyle.dashed]) In other words: we should not use *args anymore. Best regards, Jörg |
From: Magnus L. H. <ma...@he...> - 2004-01-13 21:47:01
|
Joerg Lehmann <jo...@us...>: > > Hi Magnus, >=20 [snip] > In other words: we should not use *args anymore. Seems fine to me. > Best regards, >=20 > J=F6rg --=20 Magnus Lie Hetland "The mind is not a vessel to be filled, http://hetland.org but a fire to be lighted." [Plutarch] |
From: Andre W. <wo...@us...> - 2004-01-14 07:41:43
|
Hi, On 13.01.04, Joerg Lehmann wrote: > You hit exactly the point. Inconsistency for the sake of some > minor notational compactness is the wrong thing to do. > > I'm now really in favour of > > - c.draw(p, color.rgb.blue) > - c.draw(p, [color.rgb.blue, deco.filled]) == > c.draw(p, attrs=[color.rgb.blue, deco.filled]) > - deco.stroked(color.rgb.blue) > - deco.stroked([color.rgb.blue, style.linestyle.dashed]) == > deco.stroked(attrs=[color.rgb.blue, style.linestyle.dashed]) > > In other words: we should not use *args anymore. Ok. Seems fine to me, although I may need some time to get used to it ;-) There are a few related things we should decide once and for all: - Shouldn't we be even more strict and disallow for single attributes not beeing a list then (like the first of the examples)? There is one strong argument: When not using the keyword-variant, it is strange, that you can write a single attribute and it works without being a list, while several attributes do not work. - Should we still mix several attribute categories (like stroke attributes versus decorators) in the attrs argument list? I think so. - What kind of "sequences" we do allow in those attributes arguments? I would suggest lists, but no tuples -- I started to hate them when using them at the wrong place. And maybe iterators. We may use a for loop (and nothing else) when evaluating those attributes arguments -- and do not take care about it in any other sence. - We may allow for attrs=None to ignore the hole command. This is usefull for cases with several attribute lists. We have to be carefull when merging default attributes to a None. Should I mention, that I'm +1 for all the three points? ;-) André -- by _ _ _ Dr. André Wobst / \ \ / ) wo...@us..., http://www.wobsta.de/ / _ \ \/\/ / PyX - High quality PostScript figures with Python & TeX (_/ \_)_/\_/ visit http://pyx.sourceforge.net/ |
From: Magnus L. H. <ma...@he...> - 2004-01-14 09:33:47
|
Andre Wobst <wo...@us...>: > > Hi, > [snip] > - Shouldn't we be even more strict and disallow for single attributes > not beeing a list then (like the first of the examples)? This seems important if one uses an attribute object (user-made) that also "looks" like a sequence (e.g. is iterable). > - What kind of "sequences" we do allow in those attributes arguments? How about simply sequences (that is, iterable objects)? No need to distinguish between types, is it? (Type checking is very un-pythonic.) > I would suggest lists, but no tuples The only thing special about tuples is that they are mutable, which is quite natural in this case anyway. It seems very unnatural to me to ban tuples as a special case. > -- I started to hate them when using them at the wrong place. You hate tuples? Interesting... ;) > And maybe iterators. We may use a for loop (and nothing else) when > evaluating those attributes arguments -- and do not take care > about it in any other sence. Right. That would also allow tuples... -- Magnus Lie Hetland "The mind is not a vessel to be filled, http://hetland.org but a fire to be lighted." [Plutarch] |
From: Andre W. <wo...@us...> - 2004-01-14 10:09:58
|
On 14.01.04, Magnus Lie Hetland wrote: > > - Shouldn't we be even more strict and disallow for single attributes > > not beeing a list then (like the first of the examples)? > > This seems important if one uses an attribute object (user-made) that > also "looks" like a sequence (e.g. is iterable). Right. > > -- I started to hate them when using them at the wrong place. > > You hate tuples? Interesting... ;) Yeah, I do. When I started python, I just had no idea, what lists and tuples are for. What I didn't understand is, that tuples should be used as a fix collection only. Like an imaginary number: there is a real and a imaginary part. Always two numbers with each having a fixed meaning. In lists, all items should have the same meaning. In tuples they must not! When you ever write "(item,)" in python, e.g. a tuple containing a single item, you are doing something completely wrong. (It already looks crazy ... ;-)) Nowadays I claim tuples are unnecessary and should be avoided. I often write a container class in all kind of (even little) projects: class container: def __init__(self, **kwargs): for key, value in kwargs.items(): setattr(self, key, value) Then I do complex_number=container(real=1, imag=2) skipping tuples at all. When I started with python and PyX, I was far away from those thoughts ... > > And maybe iterators. We may use a for loop (and nothing else) when > > evaluating those attributes arguments -- and do not take care > > about it in any other sence. > > Right. That would also allow tuples... That's fine. As long as I can still hate tuples myself ... ;-) André -- by _ _ _ Dr. André Wobst / \ \ / ) wo...@us..., http://www.wobsta.de/ / _ \ \/\/ / PyX - High quality PostScript figures with Python & TeX (_/ \_)_/\_/ visit http://pyx.sourceforge.net/ |
From: Magnus L. H. <ma...@he...> - 2004-01-14 10:42:12
|
Andre Wobst <wo...@us...>: > [snip] > > You hate tuples? Interesting... ;) >=20 > Yeah, I do. When I started python, I just had no idea, what lists and > tuples are for. What I didn't understand is, that tuples should be > used as a fix collection only. Like an imaginary number: there is a > real and a imaginary part. Right. Lists are homogenous and have variable length, tuples are heterogenous and have fixed length. (Not true in most cases, of course <wink>, but I guess that's a guide to the philosophy...) > (It already looks crazy ... ;-)) Hehe. You *can* write [foo,] as well... ;) > Nowadays I claim tuples are unnecessary and should be avoided. I often > write a container class in all kind of (even little) projects: >=20 > class container: > def __init__(self, **kwargs): > for key, value in kwargs.items(): > setattr(self, key, value) How about def __init__(self, **kwargs): self.__dict__.update(kwargs) ? You don't go via properties and __setattr__ and stuff then, of course... > Then I do complex_number=3Dcontainer(real=3D1, imag=3D2) skipping tuple= s at > all. When I started with python and PyX, I was far away from those > thoughts ... I guess tuples have some uses in the interface to C as well (for performance reasons). > > > And maybe iterators. We may use a for loop (and nothing else) whe= n > > > evaluating those attributes arguments -- and do not take care > > > about it in any other sence. > >=20 > > Right. That would also allow tuples... >=20 > That's fine. As long as I can still hate tuples myself ... ;-) Of course... :) Actually, when I wrote the chapter on sequences in Practical Python, I really had problems explaining why there were tuples. I also asked Alex Martelli (one of my tech editors) and he thought they were totally unnecessary too. One of the ideas we came up with from that discussion (but which was never really implemented) was a freezing API, for freezing objects of all kinds. Then you could, at least, have the immutability of tuples, without the tuples... I almost like the Lua approach -- there they only have a dictionary-like type which is used for objects and sequences too (with numeric keys). But I guess that's a bit extreme ;) > Andr=E9 --=20 Magnus Lie Hetland "The mind is not a vessel to be filled, http://hetland.org but a fire to be lighted." [Plutarch] |
From: Andre W. <wo...@us...> - 2004-01-14 08:27:24
|
Hi, On 13.01.04, Magnus Lie Hetland wrote: > How does it currently work with more than one set of attributes, then? > Do you use several keyword arguments? Yes. For example axis painters in the graph module take a hole lot of different attributes lists (painting the baseline, the gridlines, the ticks, the labels, the axis title ...) as lists in keyword arguments. > > But consider a decorator, which fills some arbitrary > > path and writes a label into the filled area. Hence there are two > > distinct fill operations in this example. Now you would need to use > > keyword arguments taking lists of attributes with no excuse, wouldn't > > you? (In the graph module, e.g. the axis painters and graph styles, > > those constructs are quite common.) > > In a quick glance through the docs I didn't find exactly what you're > talking about. Are you saying that you have a list of lists, then? > Like > > attrs=[[foo, bar, baz], # First set of attributes > [fie, foe, fum]] # Second set of attributes > > This could, of course, be easily dealt with without the attrs kwd. > argument too: > > somedecorator([foo, bar, baz], [fie, foe, fum]) > > But I guess you're probably talking about something else...? What I was talking about was something like axispainter(labelattrs=[text.size(-2)], titleattrs=[text.halign.center]) So yes, keyword arguments best here. Let me briefly explain this stupid list of list things (I consider this a wrong design and tell you below, what I think we want to do in the future). But lets first see, that we currently have. Consider you draw a grid within a graph (e.g. lines for ticks and subticks). The idea of ticks, subticks etc. is, that there can be arbitrary levels of sub-sub-sub-ticks. There is a gridattrs attribute of the painter. You can set gridattrs=[style.linestyle.solid, color.rgb.green] turning on all grid lines of any level to be painted solid green. But thats not always wanted. You can also write: gridattrs=[[style.linestyle.solid, color.rgb.green], [style.linestyle.dashed, color.rgb.red]] For ticks the first list is used, for subticks the second one. You can also have None attributes, even for the first "list": gridattrs=[None, [style.linestyle.dashed, color.rgb.red]] which leads to grid lines for the subticks only. Although this works, we do have to provide other, so called changeable attributes, somewhere else, and could make use of it at that point as well. When you plot several functions, you want to have an automatic change of the linestyle (solid, dashed, etc.). Consider a multistroke-function, which takes a list of paths, and attributes to be used when stroking this paths: multistroke([path1, path2, path3], attrs=[color.changecolor(color.palette.rainbow)]) A palette is a construct, which returns a color depending on a parameter (in the range 0 to 1). The multistroke would evaluate the changeable attribute for the different paths separately resulting the different paths to be stroked in different colors. Those things are possible in the graph styles. We could make that techniques generic and use them for the axis painters and the different tick and label levels as well ... > > Additionally, I'm against private *args and **kwargs handling (and > > mixing them both in one method at all). > > Really? Why? (And, by the way, what do you mean by "private" in this > context?) This is kind of difficult to catch, since you need to address a case, where you want to specialize an already existing function by adding more arguments (however, this is a typical case when using inheritance). Consider a function f having only keyword arguments: f(arg1=1, arg2=2, arg3=3): pass Now you want to overwrite this method but call the inherited method. The new method may get further attributes, but pass other attributes with a variable keyword argument: g(arg4=4, arg5=5, **kwargs): f(**kwargs) You can also modify some keyword args from f: g(arg4=4, arg5=5, arg1=3, **kwargs): f(arg1=arg1, **kwargs) Not, that for all other kwargs of f you do *not* have to repeat the default values; you do not even know about them! This is very beautyfull. However, there are two things you have to restrict yourself to: - You can not mix this with variable position arguments (*args). - You can not split **kwargs to be used for several different calls. This is basically a restriction, that you can do use this technique with single inheritance only (you may use multiple inheritance, but this delegation of keyword arguments is restricted to a call of one inherited member function only). We may discuss this further (I may not have thought about all problems/possibilities well enough), but this might not belong to pyx-user (as the origional RFC question about PyX's attribute system does, although already this topic is quite technical for a users discussion list). André -- by _ _ _ Dr. André Wobst / \ \ / ) wo...@us..., http://www.wobsta.de/ / _ \ \/\/ / PyX - High quality PostScript figures with Python & TeX (_/ \_)_/\_/ visit http://pyx.sourceforge.net/ |
From: Andre W. <wo...@dp...> - 2004-01-12 09:08:41
|
Hi, Jörg, your report about the current state of the attribute system was excellent. But I do have some remarks. Right now, we decided to have decorators, which are attributes to be used to convert a path (a mathematical object) to a decorated path (something you can pass to canvas.draw), to be classes, which must be instanciated. When creating those instances, it is possible to pass appropriate attributes (stroke attributes, fill attributes) to the decorators. canvas.stroke(<path>, <stroke attribute>) was basically equal to canvas.draw(<path>, deco.stroked(<stroke attributes>). Our main question is, how we deal with those specialization in the different kind of attributes. In the current implemented version, we claim that there are no such specializations needed for stroke, fill or text attributes (like style.linewidth, style.linestyle, style.linecap, etc., colors (stroke and fill attribute at the same time), patterns (stroke and fill attribute at the same time?), etc., text.halign, text.valign, text.valign, text.vshift, etc.) For decorators the storry is totally different, since usually, when using them, you want to always specialize them. Thus you can say: canvas.draw(<path>, <common attributes for stroke and filling>, deco.stroked(<some stroke attributes>), deco.filled(<some fill attributes>)) However, this might lead to: canvas.draw(<path>, color.rgb.red, deco.stroked(), deco.filled()) Please note, that you have to create instances from stroked and filled, while color.rgb.red is already an instance. Here, color.rgb is the class, and after we defined this class, we create some instances like color.rgb.red = color.rgb(1, 0, 0). Back to the decorators we have some of those, which are really funny. Right now, the arrows are those once. They take a path and create some additional stuff around that path (they also modify the path, e.g. the path is shortend a bit). Now arrows can be different in size. It is natural to have deco.earrow.normal and things like deco.earrow.small and so on. But what are these things? Are they instances of deco.earrow already or are they still classes to be instanciate later on. Consider deco.earrow.small(deco.stroke(linestyle.solid)) (combined with stroking the path with a dashed line. This is what Magnus was asking before. So here deco.earrow.small should still be a class (like it is in the current CVS version). But then you need to say deco.earrow.small() instead of deco.earrow.small without the brakets. Well ... this is strange if you compare it to the color example, where color.rgb.red is already an instance. Originally, Jörg came up with __call__ to use the instance deco.eqrrow.small as a factory for more specialized decorators. This means also, that you could say both deco.earrow.small and deco.earrow.small()! (Note, that this was removed in the current CVS, since I argued strongly against the call stuff. I just didn't fit my (quite limited) view object oriented design. But I'm becoming more pythonic by the time ...) Jörg is trying to get some final decision on this topic before finishing 0.5. (We do break some code with 0.5 already, and it would be good, to do some final decisions at that moment to not break a lot of similar things again in the next release.) BWT I don't think, that deco.earrow.small.with(<something>) is better than deco.earrow.small(<something>), e.g. using a regular method instead of __call__. It becomes more clear by that, that deco.earrow.small is an instance already, but at the same time its more lengthly and people have to remember this "with" concept, while the other is already telling the reader, that some kind of instance is created here (when they just ignore, that deco.earrow.small is an instance already). So it seems nice to use __call__??? Well, but is it correct to restrict that to decorators than? What about attributes, which have several parameters? One could want to change the blackness of a CMYK color. Other use cases are style.linestyle and text.vshift (consider text.vshift.middlezero(char="a") --- we might rename text.vshift.(bottom|middle|top)zero to text.vshift.(bottom|middle|top)char). Shouldn't we allow for calls here as well? I'm not sure at all. It might be even more consistent. Hopefully I've added something usefull to the discussion ... thanks for reading ... André -- by _ _ _ Dr. André Wobst / \ \ / ) wo...@us..., http://www.wobsta.de/ / _ \ \/\/ / PyX - High quality PostScript figures with Python & TeX (_/ \_)_/\_/ visit http://pyx.sourceforge.net/ |