Re: newb question about @property
On 05/10/2017 14:13, Steve D'Aprano wrote: On Thu, 5 Oct 2017 10:51 pm, bartc wrote: Am I allowed to say that it all seems a bit of a mess? You may or may not be pleased to learn that there's a push to create a "record like" or "struct like" datatype for Python 3.7 or 3.8, tentatively called a "Data Class" for now. https://www.python.org/dev/peps/pep-0557/ Yeah, maybe (looks a bit over-ambitious to me; why is it necessary to have typed fields? Or to do relative compares?) But it reminds me a bit of the xkcd cartoon about the 14 competing standards becoming 15... -- bartc -- https://mail.python.org/mailman/listinfo/python-list
Re: newb question about @property
On Thu, 5 Oct 2017 10:51 pm, bartc wrote: > Am I allowed to say that it all seems a bit of a mess? You may or may not be pleased to learn that there's a push to create a "record like" or "struct like" datatype for Python 3.7 or 3.8, tentatively called a "Data Class" for now. The proposed syntax will use class syntax, to make it easy to add methods. For example: @dataclass class InventoryItem: name: str unit_price: float quantity_on_hand: int = 0 def total_cost(self) -> float: return self.unit_price * self.quantity_on_hand which will fill in the boilerplate for you: - an appropriate initialiser __init__ - a good-looking __repr__ - equality, inequality, and rich comparison operators; - an optional __hash__ method. https://www.python.org/dev/peps/pep-0557/ -- Steve “Cheer up,” they said, “things could be worse.” So I cheered up, and sure enough, things got worse. -- https://mail.python.org/mailman/listinfo/python-list
Re: newb question about @property
On 05/10/2017 12:29, Gregory Ewing wrote: bartc wrote: Result? You can't just look at my 'any' class and see what fields it uses. You can't even just look at the static source code. You have to run the program to find out. And it might be different each time. You can usually get a pretty good idea of what attributes a class has by looking at its definition. The vast majority of classes will either initialise their attributes in the __init__ method or provide defaults as class variables. While in theory it's possible for code to add attributes later after initialisation, in practice this is hardly ever done. Yeah, but, like many other things in this language, there are million ways of doing it. Just had a quick look, the first hit was talking about using a module 'pyrecord'. Another mentioned 'namedtuples'. Another used the '__slots__' method already mentioned (remembering the difference between old and new classes in Python 2...) One more uses a module 'recordtype'. And then there is just using a normal class, where you have to look closely at the code to see what it's implementing. It allow ad-hoc fields to be created, or it may define __init__ etc. And all with different capabilities regarding adding extra fields, having mutable records, initialising a record, comparing them, printing them, Am I allowed to say that it all seems a bit of a mess? -- bartc -- https://mail.python.org/mailman/listinfo/python-list
Re: newb question about @property
bartc wrote: Result? You can't just look at my 'any' class and see what fields it uses. You can't even just look at the static source code. You have to run the program to find out. And it might be different each time. You can usually get a pretty good idea of what attributes a class has by looking at its definition. The vast majority of classes will either initialise their attributes in the __init__ method or provide defaults as class variables. While in theory it's possible for code to add attributes later after initialisation, in practice this is hardly ever done. -- Greg -- https://mail.python.org/mailman/listinfo/python-list
Re: newb question about @property
On Wed, 4 Oct 2017 11:46 pm, Rhodri James wrote: > On 04/10/17 12:07, bartc wrote: >> I've seen that example brought up before. It still doesn't cut any ice. >> >> You might as well be condescending of someone who finds Joyce or Proust >> unreadable, and prefers McBain, Simenon or Chandler. (Sorry, can't think >> of any modern pulp novelists). > > I don't think your comparison is appropriate. Joyce and Proust strike > me as the literary equivalent of Perl or APL; very clever but nearly > unreadable even for experts. No, think rather of Terry Pratchett. +1 for mentioning Sir PTerry! And an extra bonus for it actually being relevant :-) -- Steve “Cheer up,” they said, “things could be worse.” So I cheered up, and sure enough, things got worse. -- https://mail.python.org/mailman/listinfo/python-list
Re: Constants [was Re: newb question about @property]
On Thu, 5 Oct 2017 04:00 am, Paul Moore wrote: > I wonder - would the people who want "real constants" find the > following confusing: > from demo import foo foo = 14 foo > 14 > > It's fundamental to the way the import function works, and how names > in Python behave, but I can see someone with a background in other > languages with "real" constants thinking "but foo is a constant, and > importing it stops it being a constant!" Of course it would be confusing. Just as "from module import foo" can be confusing today, without constants. People are surprised by at least two things: - if foo is mutable, they may be surprised that importing it doesn't make a copy; mutating the "imported copy" will show up everywhere; - they may be surprised that the local name "foo" isn't an alias to the qualified name "demo.foo": if demo.foo changes, foo does not. So is early binding of function defaults. And in other contexts, so is late binding of function defaults. Scoping and name binding rules are something one has to learn. When I first learned Python, they caused me trouble, and I'm sure they will cause any beginner to programming trouble. Adding constants to the language won't change that. Besides, if we had constants: const foo = 1234 then we could have: from demo import const foo -- Steve “Cheer up,” they said, “things could be worse.” So I cheered up, and sure enough, things got worse. -- https://mail.python.org/mailman/listinfo/python-list
Re: newb question about @property
On Thu, 5 Oct 2017 09:08 am, bartc wrote: [...] > And when I tried, it didn't really work in Python 2 (extra attributes > could still be created, and .__slots__ wasn't readonly); only Py3. Not quite, but I don't blame you for the mistake. Its an easy one to make. __slots__ only works in "new style classes", not "classic classes" in Python 2. And yes, it is a wart in Python 2 that there are two subtly different kinds of class. This is due to historical reasons, and its fixed in Python 3. In Python 2, "classic classes" are declared like this: class MyClass: ... (or by inheriting from another classic class). New-style classes, or "types", inherit from object: class MyClass(object): ... (or some other new-style type, like int, dict, list, etc.) Yes yes yes, I completely agree that this is a suboptimal situation. It is a language wart and a trap for the beginner, or even the experienced coder. Use Python 3, where it is fixed. -- Steve “Cheer up,” they said, “things could be worse.” So I cheered up, and sure enough, things got worse. -- https://mail.python.org/mailman/listinfo/python-list
Re: newb question about @property
On 04/10/2017 17:02, Rhodri James wrote: On 04/10/17 16:33, Paul Moore wrote: It's not an advantage or a disadvantage, just an approach. Many people like it, you may not. Specifically, yes you can't "just declare a lightweight struct or record with exactly two fields". Actually you can: >>> class Point: ... __slots__ = ("x", "y") ... def __init__(self, x, y): ... self.x = x ... self.y = y ... def __str__(self): ... return "({0},{1})".format(self.x, self.y) ... >>> p = Point(3,4) >>> print(p) (3,4) >>> print(p.x) 3 >>> p.x = 7 >>> print(p) (7,4) >>> p.z = 2 Traceback (most recent call last): File "", line 1, in AttributeError: 'Point' object has no attribute 'z' I pretty much never bother to do this because (bart to the contrary) But it's something you'd have to explicitly do (unless perhaps you make use of those decorators, which AFAICS can do magic). And when I tried, it didn't really work in Python 2 (extra attributes could still be created, and .__slots__ wasn't readonly); only Py3. it isn't useful if you're thinking in Pythonic terms, I clearly don't. When I do this in my non-Python language it would be just: record any = (var x,y) p := any(10,20) println p # (10,20) println p.y, p.x # 20 10 println p.len # 2 println p[2] # 20 println p = any(10,20) # 1 (compares fields) No __slots__, __init__ or __str__ need to be defined; it just works. If I needed a version suitable for foreign function interfacing, a little different: type point = struct (real64 x,y) p := point(10,20) println p # (10.,20.) println p.bytes# 16 It's not Pythonic, but it is pretty handy (I know Python can also do structs like this but it appeared to be quite a slog last time I looked.) I guess my kind of coding is somewhat less esoteric than the kind of thing I typically see in Python here. -- bartc -- https://mail.python.org/mailman/listinfo/python-list
Re: newb question about @property
On Wed, Oct 4, 2017 14:03 PM, bartcwrote > "A property, in some object-oriented programming languages, is a special >sort of class member, intermediate in functionality between a field (or >data member) and a method." > >But Python has some problems just in using fields. If you wanted say a >data type with two fields called x and y, then AFAIK you just create any >suitable class, but you don't need to specifically declare those two fields: > > class any(): > pass > > p=any() > p.x=10 > p.y=20 > >But you also do this: > > p.z=30 > >and it magically acquires a third field! Or you want to modify p.x, but >accidentally type: > > p.c=40 > >No error. Some would perceive all this as an advantage, but it means you >can't just declare a lightweight struct or record 'Point' with exactly >two fields x and y. You have to use other solutions ('namedtuples' or >whatever, which probably are immutable so that don't work the same way). > >This is another example of neglecting the basics, but going for more >advanced, perhaps more sexy features instead. > >Result? You can't just look at my 'any' class and see what fields it >uses. You can't even just look at the static source code. You have to >run the program to find out. And it might be different each time. > > I think you might be missing a small feature buried in the language: class any():.. __slots__ = ["x","y"] >>> p = any() >>> p.x = 10 >>> p.y = 20 >>> p.z = 30 Traceback (most recent call last): File " ", line 1, in p.z = 30 AttributeError: 'any' object has no attribute 'z' >>> p.c = 40 Traceback (most recent call last): File " ", line 1, in p.c = 40 AttributeError: 'any' object has no attribute 'c' >>> p.__slots__ ['x', 'y'] So, you can prevent new fields from being added without forcing yourself into an immutable named tuple. And if you really need to know what is in there, it tells you! Oh, and as regards to it being different each time: >>> p.__slots__ = ['x','y','z'] Traceback (most recent call last): File " ", line 1, in p.__slots__ = ['x','y','z'] AttributeError: 'any' object attribute '__slots__' is read-only Oh, and here's a little thing from the Python 3.6.2 Glossary: __slots__ A declaration inside a class that saves memory by pre-declaring space for instance attributes and eliminating instance dictionaries. Though popular, the technique is somewhat tricky to get right and is best reserved for rare cases where there are large numbers of instances in a memory-critical application. Roger Christman Pennsylvania State University -- https://mail.python.org/mailman/listinfo/python-list
Re: Constants [was Re: newb question about @property]
On 4 October 2017 at 17:15, Ian Kellywrote: > On Wed, Oct 4, 2017 at 9:08 AM, Steve D'Aprano > wrote: >> But in large projects, especially those where you cannot trust every module >> in >> the project to obey the naming convention, I can see that this lack might >> contribute to the perception, if not the fact, of Python being a bit too >> unsafe for big projects. We have read-only attributes in classes, but not >> read-only names in modules. That makes me a little bit sad. > > Which brings up the point that you can hack it in if you want it. > > $ cat demo.py > import sys > > class DemoModule: > @property > def foo(self): > return 42 > > sys.modules['demo'] = DemoModule() > > $ python3 -c 'import demo; print(demo.foo); demo.foo = 14' > 42 > Traceback (most recent call last): > File "", line 1, in > AttributeError: can't set attribute I wonder - would the people who want "real constants" find the following confusing: >>> from demo import foo >>> foo = 14 >>> foo 14 It's fundamental to the way the import function works, and how names in Python behave, but I can see someone with a background in other languages with "real" constants thinking "but foo is a constant, and importing it stops it being a constant!" Paul -- https://mail.python.org/mailman/listinfo/python-list
Re: newb question about @property
On 4 October 2017 at 17:02, Rhodri Jameswrote: > Actually you can: > class Point: > ... __slots__ = ("x", "y") > ... def __init__(self, x, y): > ... self.x = x > ... self.y = y > ... def __str__(self): > ... return "({0},{1})".format(self.x, self.y) > ... p = Point(3,4) print(p) > (3,4) print(p.x) > 3 p.x = 7 print(p) > (7,4) p.z = 2 > Traceback (most recent call last): > File "", line 1, in > AttributeError: 'Point' object has no attribute 'z' > > I pretty much never bother to do this because (bart to the contrary) it > isn't useful if you're thinking in Pythonic terms, but it can be done pretty > easily. Good point. I'd forgotten that - like you say, it's not common to want to constrain things to this level in idiomatic Python code. Paul -- https://mail.python.org/mailman/listinfo/python-list
Re: Constants [was Re: newb question about @property]
On Wed, Oct 4, 2017 at 9:08 AM, Steve D'Apranowrote: > But in large projects, especially those where you cannot trust every module in > the project to obey the naming convention, I can see that this lack might > contribute to the perception, if not the fact, of Python being a bit too > unsafe for big projects. We have read-only attributes in classes, but not > read-only names in modules. That makes me a little bit sad. Which brings up the point that you can hack it in if you want it. $ cat demo.py import sys class DemoModule: @property def foo(self): return 42 sys.modules['demo'] = DemoModule() $ python3 -c 'import demo; print(demo.foo); demo.foo = 14' 42 Traceback (most recent call last): File "", line 1, in AttributeError: can't set attribute -- https://mail.python.org/mailman/listinfo/python-list
Re: newb question about @property
On 04/10/17 16:33, Paul Moore wrote: On 4 October 2017 at 16:03, bartcwrote: No error. Some would perceive all this as an advantage, but it means you can't just declare a lightweight struct or record 'Point' with exactly two fields x and y. You have to use other solutions ('namedtuples' or whatever, which probably are immutable so that don't work the same way). This is another example of neglecting the basics, but going for more advanced, perhaps more sexy features instead. It's another example of a consistent design philosophy (highly dynamic classes) that you might not like - possibly even enough that Python isn't the best language for you. It's not an advantage or a disadvantage, just an approach. Many people like it, you may not. Specifically, yes you can't "just declare a lightweight struct or record with exactly two fields". Actually you can: >>> class Point: ... __slots__ = ("x", "y") ... def __init__(self, x, y): ... self.x = x ... self.y = y ... def __str__(self): ... return "({0},{1})".format(self.x, self.y) ... >>> p = Point(3,4) >>> print(p) (3,4) >>> print(p.x) 3 >>> p.x = 7 >>> print(p) (7,4) >>> p.z = 2 Traceback (most recent call last): File "", line 1, in AttributeError: 'Point' object has no attribute 'z' I pretty much never bother to do this because (bart to the contrary) it isn't useful if you're thinking in Pythonic terms, but it can be done pretty easily. -- Rhodri James *-* Kynesim Ltd -- https://mail.python.org/mailman/listinfo/python-list
Re: newb question about @property
On 4 October 2017 at 16:03, bartcwrote: > No error. Some would perceive all this as an advantage, but it means you > can't just declare a lightweight struct or record 'Point' with exactly two > fields x and y. You have to use other solutions ('namedtuples' or whatever, > which probably are immutable so that don't work the same way). > > This is another example of neglecting the basics, but going for more > advanced, perhaps more sexy features instead. It's another example of a consistent design philosophy (highly dynamic classes) that you might not like - possibly even enough that Python isn't the best language for you. It's not an advantage or a disadvantage, just an approach. Many people like it, you may not. Specifically, yes you can't "just declare a lightweight struct or record with exactly two fields". Python doesn't enforce things like that, but leaves it to the programmer(s) to agree on (and follow) conventions in the code. This means that certain classes of error (e.g. mistyping an attribute name) can go unnoticed until later than in other languages, but conversely it means that things like monkeypatching of 3rd party code are possible. The popularity of Python is evidence that the flexibility this allows is useful to many people. Expecting Python to have the same design as other languages does a disservice to both Python and those other languages. There are trade-offs in these things, and Python's choices won't be the best in all circumstances. All we can really say is that they have turned out to be pretty useful and popular in many situations... Paul -- https://mail.python.org/mailman/listinfo/python-list
Constants [was Re: newb question about @property]
On Thu, 5 Oct 2017 12:41 am, Ian Kelly wrote: > Python has the simplest named constants of all: > > C = 12345 > > As long as you don't subsequently change it, it's a constant. And it's > very simple because it works just like any other variable. I do feel that the lack of constants[1] is a (mild) weakness of Python. In small projects, if you want a constant: pi = 3.1415 and you don't want to change it, it is easy enough to remember to just not change it. Especially if you follow a naming convention like using ALLCAPS for constants. But in large projects, especially those where you cannot trust every module in the project to obey the naming convention, I can see that this lack might contribute to the perception, if not the fact, of Python being a bit too unsafe for big projects. We have read-only attributes in classes, but not read-only names in modules. That makes me a little bit sad. Back in the day when I used Pascal regularly, I recall having the compiler complain when I accidentally tried to assign a new value to a constant. But I was a much less experienced coder back then. In all honesty, I can't remember the last time I accidentally reassigned to something intended as a constant. Nevertheless, constants are a safety net I would appreciate. [1] By which I mean names which can only be bound once, but not rebound. This is not about mutability, it is about whether or not the name can be rebound. -- Steve “Cheer up,” they said, “things could be worse.” So I cheered up, and sure enough, things got worse. -- https://mail.python.org/mailman/listinfo/python-list
Re: newb question about @property
On 04/10/2017 14:41, Ian Kelly wrote: On Wed, Oct 4, 2017 at 5:07 AM, bartcwrote: For that matter, I don't think Python has such a feature either. So that you write for example: const C = 123345 and then whenever C appears within the code, it's implemented as: LOAD_CONST (123345) Python has the simplest named constants of all: C = 12345 As long as you don't subsequently change it, it's a constant. And it's very simple because it works just like any other variable. Yes, but you don't want it to work like other variables! In other languages, a known compile-time constant is essential in certain contexts, but even in Python there are advantages to such a feature, for example in more opportunities for optimisation. Even if it's just so it can use LOAD_CONST instead of LOAD_GLOBAL (as such constants often are global). The problems with doing this in current CPython are first, that all such identifiers are dynamic: you can assign anything to them at any time. Also that some may be inside imported files, which the byte-code compiler will not know about until its too late. (There are ways around those but it would be an impossible sell when most people are not convinced that lightweight named constants are useful.) I'm trying to think of a real example where I've had to add a cache to to a function, whatever that even means (memoisation?). You've never used a dynamic programming algorithm? I had to look up what it means, but I still didn't see any practical examples. Probably I've done such coding, but I didn't give it a fancy name, and more likely just considered it data caching. > Descriptors are a bit unique to Python, I'll grant, but mostly they're > just used in the form of properties. Here's a list of languages that > support properties: > https://en.wikipedia.org/wiki/Property_(programming)#Example_syntax "A property, in some object-oriented programming languages, is a special sort of class member, intermediate in functionality between a field (or data member) and a method." But Python has some problems just in using fields. If you wanted say a data type with two fields called x and y, then AFAIK you just create any suitable class, but you don't need to specifically declare those two fields: class any(): pass p=any() p.x=10 p.y=20 But you also do this: p.z=30 and it magically acquires a third field! Or you want to modify p.x, but accidentally type: p.c=40 No error. Some would perceive all this as an advantage, but it means you can't just declare a lightweight struct or record 'Point' with exactly two fields x and y. You have to use other solutions ('namedtuples' or whatever, which probably are immutable so that don't work the same way). This is another example of neglecting the basics, but going for more advanced, perhaps more sexy features instead. Result? You can't just look at my 'any' class and see what fields it uses. You can't even just look at the static source code. You have to run the program to find out. And it might be different each time. -- bartc -- https://mail.python.org/mailman/listinfo/python-list
Re: newb question about @property
On Wed, Oct 4, 2017 at 5:07 AM, bartcwrote: > It is just being elitist. I have a preference for keeping things simple and > avoiding unnecessary complexity. But with programming languages many do have > a penchant for the latter. > > As an example, a recent discussion on comp.lang.c was about implementing > named constants. I proposed one very simple way of doing it, other people > were talking about using #define, enum, const, static const, or importing > constexpr and special rules for 'const' from C++. All unsatisfactory and > each having their own problems. > > For that matter, I don't think Python has such a feature either. So that you > write for example: > > const C = 123345 > > and then whenever C appears within the code, it's implemented as: > > LOAD_CONST (123345) > > I'm pretty sure that there are very complicated ways of achieving something > similar, maybe with all your decorators, or using PyMacro or whatever. But > not doing it straightforwardly. [Python's design makes a simple > implementation harder.] Python has the simplest named constants of all: C = 12345 As long as you don't subsequently change it, it's a constant. And it's very simple because it works just like any other variable. Python also has a particularly flexible Enum implementation, but if you don't want it then don't use it. >> You don't think that adding a cache for an expensive function is >> programming? >> >> If you had ten expensive functions, and you wanted to add a cache to each >> of >> them, would you write out ten separate caches (probably copying and >> pasting >> the code each time)? >> >> Or would you write a function that builds a cache and adds it to the >> expensive >> function *once*, then call it ten times, once for each function being >> wrapped? > > > I'm trying to think of a real example where I've had to add a cache to to a > function, whatever that even means (memoisation?). You've never used a dynamic programming algorithm? >> That you think that this is not programming is an indictment off your >> programming skills. These sorts of functional programming techniques go >> back >> to the 1950s, Lisp is literally the second oldest high-level language ever >> (only Fortran is older). You may or may not have been a hotshot in your >> little corner of the programming world, but there's an entire world out >> there. > > > What other languages apart from Python have equivalent features to > decorators and, what's the other one, descriptors? Apart from Lisp. Literally any language with first-class function types, whether they have the @decorator-style syntactic sugar for it or not. Descriptors are a bit unique to Python, I'll grant, but mostly they're just used in the form of properties. Here's a list of languages that support properties: https://en.wikipedia.org/wiki/Property_(programming)#Example_syntax -- https://mail.python.org/mailman/listinfo/python-list
Re: newb question about @property
On 04/10/17 12:07, bartc wrote: I've seen that example brought up before. It still doesn't cut any ice. You might as well be condescending of someone who finds Joyce or Proust unreadable, and prefers McBain, Simenon or Chandler. (Sorry, can't think of any modern pulp novelists). I don't think your comparison is appropriate. Joyce and Proust strike me as the literary equivalent of Perl or APL; very clever but nearly unreadable even for experts. No, think rather of Terry Pratchett. (Almost) anyone can read a Pratchett novel and enjoy it. Most people will not even notice maybe half the jokes. This is most obvious with The Colour of Magic and The Light Fantastic, where long-time Fantasy readers will spot Fafhrd and the Grey Mouser, recognise the tropes that are cheerfully being exploded, and so on. You can write decent Python without using decorators, properties, metaclasses or comprehensions in much the same way that you can enjoy Pratchett without ever having heard of Fritz Lieber. For a straightforward enough problem, writing your Python as if it was C won't even cause you any trouble. But if you think using these extra tools isn't programming, you are as flat out wrong as if you think Small Gods is just about a deity having to work on being believed in. -- Rhodri James *-* Kynesim Ltd -- https://mail.python.org/mailman/listinfo/python-list
Re: newb question about @property
On 04/10/2017 06:32, Steve D'Aprano wrote: On Wed, 4 Oct 2017 02:00 am, bartc wrote: Does all this advanced stuff (which I don't understand and which doesn't look very appealing either; hopefully I will never come across such code) still count as programming? I could not have hoped to see a more perfect example of the Blub effect in action if I had invented it myself. As long as our hypothetical Blub programmer is looking down the power continuum, he knows he's looking down. Languages less powerful than Blub are obviously less powerful, because they're missing some feature he's used to. But when our hypothetical Blub programmer looks in the other direction, up the power continuum, he doesn't realize he's looking up. What he sees are merely weird languages. He probably considers them about equivalent in power to Blub, but with all this other hairy stuff thrown in as well. Blub is good enough for him, because he thinks in Blub. http://www.paulgraham.com/avg.html I've seen that example brought up before. It still doesn't cut any ice. You might as well be condescending of someone who finds Joyce or Proust unreadable, and prefers McBain, Simenon or Chandler. (Sorry, can't think of any modern pulp novelists). It is just being elitist. I have a preference for keeping things simple and avoiding unnecessary complexity. But with programming languages many do have a penchant for the latter. As an example, a recent discussion on comp.lang.c was about implementing named constants. I proposed one very simple way of doing it, other people were talking about using #define, enum, const, static const, or importing constexpr and special rules for 'const' from C++. All unsatisfactory and each having their own problems. For that matter, I don't think Python has such a feature either. So that you write for example: const C = 123345 and then whenever C appears within the code, it's implemented as: LOAD_CONST (123345) I'm pretty sure that there are very complicated ways of achieving something similar, maybe with all your decorators, or using PyMacro or whatever. But not doing it straightforwardly. [Python's design makes a simple implementation harder.] Anyway, what I'm saying is that languages sometimes neglect the basics in favour of all this other stuff. You don't think that adding a cache for an expensive function is programming? If you had ten expensive functions, and you wanted to add a cache to each of them, would you write out ten separate caches (probably copying and pasting the code each time)? Or would you write a function that builds a cache and adds it to the expensive function *once*, then call it ten times, once for each function being wrapped? I'm trying to think of a real example where I've had to add a cache to to a function, whatever that even means (memoisation?). Is it looking to see if a certain combination of parameters has been used before and retrieving the return value that resulted on that occasion without having to redo the calculation? In my sort of coding that would need be done on a per-function basis because there would be other considerations. It does still sound like adding a turbo-charger to your engine, rather than actually going anywhere. That at least might be useful, but it's still not driving. But if the ultimate purpose is more speed, then piling on yet more code and more layers may be counter-productive! That you think that this is not programming is an indictment off your programming skills. These sorts of functional programming techniques go back to the 1950s, Lisp is literally the second oldest high-level language ever (only Fortran is older). You may or may not have been a hotshot in your little corner of the programming world, but there's an entire world out there. What other languages apart from Python have equivalent features to decorators and, what's the other one, descriptors? Apart from Lisp. -- bartc -- https://mail.python.org/mailman/listinfo/python-list
Re: newb question about @property
On Wed, 4 Oct 2017 02:00 am, bartc wrote: > Does all this advanced stuff (which I don't understand and which doesn't > look very appealing either; hopefully I will never come across such > code) still count as programming? I could not have hoped to see a more perfect example of the Blub effect in action if I had invented it myself. As long as our hypothetical Blub programmer is looking down the power continuum, he knows he's looking down. Languages less powerful than Blub are obviously less powerful, because they're missing some feature he's used to. But when our hypothetical Blub programmer looks in the other direction, up the power continuum, he doesn't realize he's looking up. What he sees are merely weird languages. He probably considers them about equivalent in power to Blub, but with all this other hairy stuff thrown in as well. Blub is good enough for him, because he thinks in Blub. http://www.paulgraham.com/avg.html > It seems to me the equivalent of an advanced driving course teaching you > how to customise your car rather than involving any actual driving. You don't think that adding a cache for an expensive function is programming? If you had ten expensive functions, and you wanted to add a cache to each of them, would you write out ten separate caches (probably copying and pasting the code each time)? Or would you write a function that builds a cache and adds it to the expensive function *once*, then call it ten times, once for each function being wrapped? My examples didn't include a full-blown cache, just the beginnings of one, but the principle still stands. Program smart, not hard: don't do things by hand if you can write a function to do them. That you think that this is not programming is an indictment off your programming skills. These sorts of functional programming techniques go back to the 1950s, Lisp is literally the second oldest high-level language ever (only Fortran is older). You may or may not have been a hotshot in your little corner of the programming world, but there's an entire world out there. -- Steve “Cheer up,” they said, “things could be worse.” So I cheered up, and sure enough, things got worse. -- https://mail.python.org/mailman/listinfo/python-list
Re: newb question about @property
On Tue, Oct 3, 2017 at 9:16 AM, Jorge Gimenowrote: > No, I see this as teaching the skills involved to drive a car. Practicing a > turn, scanning gauges, and checking blind spots are all a part of driving. > When one is learning, it's easier to learn these in isolation so when the > problem must be solved in real time, you know what to do. This is no > different. You may never need to use a decorator ever in your development > career, but the tool is there in case the problem you have can be elegantly > solved using one. I have to agree. Learning the language without taking the time to learn all of its features is like learning to drive without bothering to learn how to use the tachometer or the hand brake. Sure you can drive without those things, but that doesn't make them not useful. -- https://mail.python.org/mailman/listinfo/python-list
Re: newb question about @property
Steve D'Aprano wrote: On Tue, 3 Oct 2017 06:51 am, Bill wrote: Can you inspire me with a good decorator problem (standard homework exercise-level will be fine)? Here is a nice even dozen problems for you. Please ask for clarification if any are unclear. Thank you for sharing the problems on decorators! I just finished reading Bruce Eckels' note on decorators (as well as some of the comments left by readers), which you shared a link to, and found the whole discussion very informative. If I was to point to two details, the analogy of decorator's with macros is helpful to bear in mind, as is the remark that "@Wrapper(def f..)" reassigns f to the composition. With the latter unstated, the matter can cause confusion! %-) Bill (1) Write a decorator which simply prints a descriptive message and the name of the decorated function once, when the function is first decorated. E.g. if you write: @decorate def spam(x): return x + 1 # for example print(spam(1)) print(spam(2)) Python should print: Decorating function spam. 2 3 Note: "spam" must not be hard-coded, it must be taken from the function being decorated. (Hint: all functions have their name available as func.__name__.) (2) Modify the decorator from (1) so that calling the wrapped function also print a descriptive message such as "Calling function spam". The expected output will be: Decorating function spam. Calling function spam. 2 Calling function spam. 3 (3) Write a decorator that checks that the decorated function's first argument is a non-empty string, raising an appropriate exception if it is not, and lets through any other arguments unchanged. (4) Same as above, except the first argument is automatically stripped of leading and trailing whitespace and forced to uppercase. (5) Write a decorator which injects the argument 10 into the list of arguments received by the wrapped function. E.g. if you write: @inject def add(a, b): return a + b @inject def sub(a, b): return a - b print(add(5), sub(5)) Python should print "15 5". (And *not* "15 -5".) (6) [ADVANCED] Modify the decorator in (5) so that it takes an argument telling it what value to inject into the list of arguments: @inject(99) def sub(a, b): return a - b print(sub(5)) will now print "94". (7) Write a decorator which checks the decorated function's two arguments are given smallest first, swapping them around if needed. (8) Write a decorator which prints the name of the wrapped function, its arguments, and the time, each time the wrapped function is called. (9) [ADVANCED] Modify the decorator from (8) to take an argument specifying the path to a file, and use the logging module to log the details to that file instead of printing them. (10) Write a decorator which adds an "cache" attribute initialised to an empty dictionary to the decorated function. (11) Write a decorator which wraps a class (not function!), and adds a "help" method to the class which prints a message as shown below. For example: @addhelp class Spam: pass @addhelp class Eggs: pass x = Spam() x.help() y = Eggs() y.help() will print: See http://example.com/Spam See http://example.com/Eggs (Hint: classes also have a __name__ attribute.) (12) [ADVANCED] Write a decorator which wraps a class, and applies the decorator from (10) above to each non-dunder¹ method in the class. That is, after: @addcaches class MyClass: def foo(self): pass def bar(self): pass print(MyClass.foo.cache, MyClass.bar.cache) should print "{} {}". ¹ Remember that dunder methods are those that start with two leading and trailing underscores: "Double UNDERscore" methods. * * * Bruce Eckel has an excellent introduction to Python decorators, from way back when they were first introduced in 2008. His introduction is notable because: - he points out explicitly that Python decorators are not the same as the Decorator design pattern (I thought they were!); - he recommends using a class as the decorator, and building the extra functionality in object oriented fashion, rather than functional programming fashion (this may give an easier introduction to those who aren't familiar with functional idioms); - and he correctly predicted that the introduction of the @ syntactic sugar would have a big impact on the way people think about Python code. http://www.artima.com/weblogs/viewpost.jsp?thread=240808 Feel free to read his post before trying the problems I set. -- https://mail.python.org/mailman/listinfo/python-list
Re: newb question about @property
No, I see this as teaching the skills involved to drive a car. Practicing a turn, scanning gauges, and checking blind spots are all a part of driving. When one is learning, it's easier to learn these in isolation so when the problem must be solved in real time, you know what to do. This is no different. You may never need to use a decorator ever in your development career, but the tool is there in case the problem you have can be elegantly solved using one. -Jorge On Tue, Oct 3, 2017 at 8:00 AM, bartcwrote: > On 03/10/2017 15:39, Ian Kelly wrote: > >> On Tue, Oct 3, 2017 at 4:41 AM, Steve D'Aprano >> wrote: >> >>> On Tue, 3 Oct 2017 06:51 am, Bill wrote: >>> >>> Can you inspire me with a good decorator problem (standard homework exercise-level will be fine)? >>> >>> >>> Here is a nice even dozen problems for you. Please ask for clarification >>> if any >>> are unclear. >>> >>> >>> >>> (1) Write a decorator which simply prints a descriptive message and the >>> name of >>> the decorated function once, when the function is first decorated. >>> >>> E.g. if you write: >>> >>> @decorate >>> def spam(x): >>> return x + 1 # for example >>> >>> print(spam(1)) >>> print(spam(2)) >>> >>> >>> Python should print: >>> >>> Decorating function spam. >>> 2 >>> 3 >>> >>> >>> Note: "spam" must not be hard-coded, it must be taken from the function >>> being >>> decorated. (Hint: all functions have their name available as >>> func.__name__.) >>> >>> >>> (2) Modify the decorator from (1) so that calling the wrapped function >>> also >>> print a descriptive message such as "Calling function spam". The expected >>> output will be: >>> >>> Decorating function spam. >>> Calling function spam. >>> 2 >>> Calling function spam. >>> 3 >>> >>> >>> (3) Write a decorator that checks that the decorated function's first >>> argument >>> is a non-empty string, raising an appropriate exception if it is not, >>> and lets >>> through any other arguments unchanged. >>> >>> >>> (4) Same as above, except the first argument is automatically stripped of >>> leading and trailing whitespace and forced to uppercase. >>> >>> >>> (5) Write a decorator which injects the argument 10 into the list of >>> arguments >>> received by the wrapped function. E.g. if you write: >>> >>> @inject >>> def add(a, b): >>> return a + b >>> >>> @inject >>> def sub(a, b): >>> return a - b >>> >>> print(add(5), sub(5)) >>> >>> Python should print "15 5". (And *not* "15 -5".) >>> >>> >>> (6) [ADVANCED] Modify the decorator in (5) so that it takes an argument >>> telling >>> it what value to inject into the list of arguments: >>> >>> @inject(99) >>> def sub(a, b): >>> return a - b >>> >>> print(sub(5)) >>> >>> will now print "94". >>> >>> >>> (7) Write a decorator which checks the decorated function's two >>> arguments are >>> given smallest first, swapping them around if needed. >>> >>> >>> (8) Write a decorator which prints the name of the wrapped function, its >>> arguments, and the time, each time the wrapped function is called. >>> >>> >>> (9) [ADVANCED] Modify the decorator from (8) to take an argument >>> specifying the >>> path to a file, and use the logging module to log the details to that >>> file >>> instead of printing them. >>> >>> >>> (10) Write a decorator which adds an "cache" attribute initialised to an >>> empty >>> dictionary to the decorated function. >>> >>> >>> (11) Write a decorator which wraps a class (not function!), and adds a >>> "help" >>> method to the class which prints a message as shown below. For example: >>> >>> @addhelp >>> class Spam: >>> pass >>> >>> @addhelp >>> class Eggs: >>> pass >>> >>> x = Spam() >>> x.help() >>> y = Eggs() >>> y.help() >>> >>> will print: >>> >>> See http://example.com/Spam >>> See http://example.com/Eggs >>> >>> (Hint: classes also have a __name__ attribute.) >>> >>> >>> (12) [ADVANCED] Write a decorator which wraps a class, and applies the >>> decorator >>> from (10) above to each non-dunder¹ method in the class. That is, after: >>> >>> @addcaches >>> class MyClass: >>> def foo(self): >>> pass >>> def bar(self): >>> pass >>> >>> print(MyClass.foo.cache, MyClass.bar.cache) >>> >>> should print "{} {}". >>> >>> >>> >>> ¹ Remember that dunder methods are those that start with two leading and >>> trailing underscores: "Double UNDERscore" methods. >>> >> > [Sorry can't see Steve's original post.] > > Does all this advanced stuff (which I don't understand and which doesn't > look very appealing either; hopefully I will never come across such code) > still count as programming? > > It seems to me the equivalent of an advanced driving course teaching you > how to customise your car rather than involving any actual driving. > > -- > bartc > -- > https://mail.python.org/mailman/listinfo/python-list > -- https://mail.python.org/mailman/listinfo/python-list
Re: newb question about @property
On 03/10/2017 15:39, Ian Kelly wrote: On Tue, Oct 3, 2017 at 4:41 AM, Steve D'Apranowrote: On Tue, 3 Oct 2017 06:51 am, Bill wrote: Can you inspire me with a good decorator problem (standard homework exercise-level will be fine)? Here is a nice even dozen problems for you. Please ask for clarification if any are unclear. (1) Write a decorator which simply prints a descriptive message and the name of the decorated function once, when the function is first decorated. E.g. if you write: @decorate def spam(x): return x + 1 # for example print(spam(1)) print(spam(2)) Python should print: Decorating function spam. 2 3 Note: "spam" must not be hard-coded, it must be taken from the function being decorated. (Hint: all functions have their name available as func.__name__.) (2) Modify the decorator from (1) so that calling the wrapped function also print a descriptive message such as "Calling function spam". The expected output will be: Decorating function spam. Calling function spam. 2 Calling function spam. 3 (3) Write a decorator that checks that the decorated function's first argument is a non-empty string, raising an appropriate exception if it is not, and lets through any other arguments unchanged. (4) Same as above, except the first argument is automatically stripped of leading and trailing whitespace and forced to uppercase. (5) Write a decorator which injects the argument 10 into the list of arguments received by the wrapped function. E.g. if you write: @inject def add(a, b): return a + b @inject def sub(a, b): return a - b print(add(5), sub(5)) Python should print "15 5". (And *not* "15 -5".) (6) [ADVANCED] Modify the decorator in (5) so that it takes an argument telling it what value to inject into the list of arguments: @inject(99) def sub(a, b): return a - b print(sub(5)) will now print "94". (7) Write a decorator which checks the decorated function's two arguments are given smallest first, swapping them around if needed. (8) Write a decorator which prints the name of the wrapped function, its arguments, and the time, each time the wrapped function is called. (9) [ADVANCED] Modify the decorator from (8) to take an argument specifying the path to a file, and use the logging module to log the details to that file instead of printing them. (10) Write a decorator which adds an "cache" attribute initialised to an empty dictionary to the decorated function. (11) Write a decorator which wraps a class (not function!), and adds a "help" method to the class which prints a message as shown below. For example: @addhelp class Spam: pass @addhelp class Eggs: pass x = Spam() x.help() y = Eggs() y.help() will print: See http://example.com/Spam See http://example.com/Eggs (Hint: classes also have a __name__ attribute.) (12) [ADVANCED] Write a decorator which wraps a class, and applies the decorator from (10) above to each non-dunder¹ method in the class. That is, after: @addcaches class MyClass: def foo(self): pass def bar(self): pass print(MyClass.foo.cache, MyClass.bar.cache) should print "{} {}". ¹ Remember that dunder methods are those that start with two leading and trailing underscores: "Double UNDERscore" methods. [Sorry can't see Steve's original post.] Does all this advanced stuff (which I don't understand and which doesn't look very appealing either; hopefully I will never come across such code) still count as programming? It seems to me the equivalent of an advanced driving course teaching you how to customise your car rather than involving any actual driving. -- bartc -- https://mail.python.org/mailman/listinfo/python-list
Re: newb question about @property
On Tue, Oct 3, 2017 at 4:41 AM, Steve D'Apranowrote: > On Tue, 3 Oct 2017 06:51 am, Bill wrote: > >> Can you inspire me with a good decorator problem (standard homework >> exercise-level will be fine)? > > > Here is a nice even dozen problems for you. Please ask for clarification if > any > are unclear. > > > > (1) Write a decorator which simply prints a descriptive message and the name > of > the decorated function once, when the function is first decorated. > > E.g. if you write: > > @decorate > def spam(x): > return x + 1 # for example > > print(spam(1)) > print(spam(2)) > > > Python should print: > > Decorating function spam. > 2 > 3 > > > Note: "spam" must not be hard-coded, it must be taken from the function being > decorated. (Hint: all functions have their name available as func.__name__.) > > > (2) Modify the decorator from (1) so that calling the wrapped function also > print a descriptive message such as "Calling function spam". The expected > output will be: > > Decorating function spam. > Calling function spam. > 2 > Calling function spam. > 3 > > > (3) Write a decorator that checks that the decorated function's first argument > is a non-empty string, raising an appropriate exception if it is not, and lets > through any other arguments unchanged. > > > (4) Same as above, except the first argument is automatically stripped of > leading and trailing whitespace and forced to uppercase. > > > (5) Write a decorator which injects the argument 10 into the list of arguments > received by the wrapped function. E.g. if you write: > > @inject > def add(a, b): > return a + b > > @inject > def sub(a, b): > return a - b > > print(add(5), sub(5)) > > Python should print "15 5". (And *not* "15 -5".) > > > (6) [ADVANCED] Modify the decorator in (5) so that it takes an argument > telling > it what value to inject into the list of arguments: > > @inject(99) > def sub(a, b): > return a - b > > print(sub(5)) > > will now print "94". > > > (7) Write a decorator which checks the decorated function's two arguments are > given smallest first, swapping them around if needed. > > > (8) Write a decorator which prints the name of the wrapped function, its > arguments, and the time, each time the wrapped function is called. > > > (9) [ADVANCED] Modify the decorator from (8) to take an argument specifying > the > path to a file, and use the logging module to log the details to that file > instead of printing them. > > > (10) Write a decorator which adds an "cache" attribute initialised to an empty > dictionary to the decorated function. > > > (11) Write a decorator which wraps a class (not function!), and adds a "help" > method to the class which prints a message as shown below. For example: > > @addhelp > class Spam: > pass > > @addhelp > class Eggs: > pass > > x = Spam() > x.help() > y = Eggs() > y.help() > > will print: > > See http://example.com/Spam > See http://example.com/Eggs > > (Hint: classes also have a __name__ attribute.) > > > (12) [ADVANCED] Write a decorator which wraps a class, and applies the > decorator > from (10) above to each non-dunder¹ method in the class. That is, after: > > @addcaches > class MyClass: > def foo(self): > pass > def bar(self): > pass > > print(MyClass.foo.cache, MyClass.bar.cache) > > should print "{} {}". > > > > ¹ Remember that dunder methods are those that start with two leading and > trailing underscores: "Double UNDERscore" methods. I also suggest: (13) Modify the decorator from (8) so that the wrapper has the same name and doc string as the wrapped function. (Hint: use functools.wraps) -- https://mail.python.org/mailman/listinfo/python-list
Re: newb question about @property
On Tue, 3 Oct 2017 10:01 pm, Lele Gaifax wrote: > Steve D'Apranowrites: > >> (9) [ADVANCED] Modify the decorator from (8) to take an argument specifying >> the path to a file, and use the logging module to log the details to that >> file instead of printing them. > > This may suffer of excessive creativity, as usually the details of *where* a > logger writes the messages are better left to a configuration done at another > level :) Right. I didn't say the path has to be hard-coded in the source. @addlogging(config.logfile or default_logfile) def function(x, y, z): ... In production code, I'd probably pass a logger instance rather than a file name. But in any case, its only a programming exercise, not meant to be production-ready code. -- Steve “Cheer up,” they said, “things could be worse.” So I cheered up, and sure enough, things got worse. -- https://mail.python.org/mailman/listinfo/python-list
Re: newb question about @property
Steve D'Apranowrites: > (9) [ADVANCED] Modify the decorator from (8) to take an argument specifying > the > path to a file, and use the logging module to log the details to that file > instead of printing them. This may suffer of excessive creativity, as usually the details of *where* a logger writes the messages are better left to a configuration done at another level :) ciao, lele. -- nickname: Lele Gaifax | Quando vivrò di quello che ho pensato ieri real: Emanuele Gaifas | comincerò ad aver paura di chi mi copia. l...@metapensiero.it | -- Fortunato Depero, 1929. -- https://mail.python.org/mailman/listinfo/python-list
Re: newb question about @property
On Tue, 3 Oct 2017 06:51 am, Bill wrote: > Can you inspire me with a good decorator problem (standard homework > exercise-level will be fine)? Here is a nice even dozen problems for you. Please ask for clarification if any are unclear. (1) Write a decorator which simply prints a descriptive message and the name of the decorated function once, when the function is first decorated. E.g. if you write: @decorate def spam(x): return x + 1 # for example print(spam(1)) print(spam(2)) Python should print: Decorating function spam. 2 3 Note: "spam" must not be hard-coded, it must be taken from the function being decorated. (Hint: all functions have their name available as func.__name__.) (2) Modify the decorator from (1) so that calling the wrapped function also print a descriptive message such as "Calling function spam". The expected output will be: Decorating function spam. Calling function spam. 2 Calling function spam. 3 (3) Write a decorator that checks that the decorated function's first argument is a non-empty string, raising an appropriate exception if it is not, and lets through any other arguments unchanged. (4) Same as above, except the first argument is automatically stripped of leading and trailing whitespace and forced to uppercase. (5) Write a decorator which injects the argument 10 into the list of arguments received by the wrapped function. E.g. if you write: @inject def add(a, b): return a + b @inject def sub(a, b): return a - b print(add(5), sub(5)) Python should print "15 5". (And *not* "15 -5".) (6) [ADVANCED] Modify the decorator in (5) so that it takes an argument telling it what value to inject into the list of arguments: @inject(99) def sub(a, b): return a - b print(sub(5)) will now print "94". (7) Write a decorator which checks the decorated function's two arguments are given smallest first, swapping them around if needed. (8) Write a decorator which prints the name of the wrapped function, its arguments, and the time, each time the wrapped function is called. (9) [ADVANCED] Modify the decorator from (8) to take an argument specifying the path to a file, and use the logging module to log the details to that file instead of printing them. (10) Write a decorator which adds an "cache" attribute initialised to an empty dictionary to the decorated function. (11) Write a decorator which wraps a class (not function!), and adds a "help" method to the class which prints a message as shown below. For example: @addhelp class Spam: pass @addhelp class Eggs: pass x = Spam() x.help() y = Eggs() y.help() will print: See http://example.com/Spam See http://example.com/Eggs (Hint: classes also have a __name__ attribute.) (12) [ADVANCED] Write a decorator which wraps a class, and applies the decorator from (10) above to each non-dunder¹ method in the class. That is, after: @addcaches class MyClass: def foo(self): pass def bar(self): pass print(MyClass.foo.cache, MyClass.bar.cache) should print "{} {}". ¹ Remember that dunder methods are those that start with two leading and trailing underscores: "Double UNDERscore" methods. * * * Bruce Eckel has an excellent introduction to Python decorators, from way back when they were first introduced in 2008. His introduction is notable because: - he points out explicitly that Python decorators are not the same as the Decorator design pattern (I thought they were!); - he recommends using a class as the decorator, and building the extra functionality in object oriented fashion, rather than functional programming fashion (this may give an easier introduction to those who aren't familiar with functional idioms); - and he correctly predicted that the introduction of the @ syntactic sugar would have a big impact on the way people think about Python code. http://www.artima.com/weblogs/viewpost.jsp?thread=240808 Feel free to read his post before trying the problems I set. -- Steve “Cheer up,” they said, “things could be worse.” So I cheered up, and sure enough, things got worse. -- https://mail.python.org/mailman/listinfo/python-list
Re: newb question about @property
Bill wrote: Chris Angelico wrote: Decorators are fairly straight-forward if you understand higher-order functions. ChrisA I was just minding my own business, and thought to write my first decorator for a simple *recursive* function f. The decorator WORKS if f does not make a call to itself (it really wasn't). Using the (PyCharm) debugger, I determined that my inner function that was calling my wrapped (Fibonacci sequence) function but wasn't returning anything to the invoking environment. I fixed it for the sake of a good, if costly, lesson. Not much of a wrapper, but it "works". A version which merely prints a tab before the function call, instead of a row of asterisks produces output which is more interesting to look at. def wrap(func): def inner(*args, **kwargs): print('*'*20) a= func(*args, **kwargs) print(a) print('*'*20) return a return inner Bill -- https://mail.python.org/mailman/listinfo/python-list
Re: newb question about @property
On Tue, Oct 3, 2017 at 2:39 PM, Billwrote: > Chris Angelico wrote: >> >> Decorators are fairly straight-forward if you understand higher-order >> functions. >> >> ChrisA > > > > I was just minding my own business, and thought to write my first decorator > for a simple *recursive* function f. The decorator WORKS if f does not make > a call to itself.Otherwise, f seems to have "difficulty" calling itself > (I get a typerror, f(n) has value "NoneType"). What is the explanation for > this? Does f have a new name because it has a decorator on it now? > > Note: I am not using functools.wraps since I don't yet understand the reason > I might do that yet (first things first... ). I would recommend posting your code, possibly in a new thread. ChrisA -- https://mail.python.org/mailman/listinfo/python-list
Re: newb question about @property
Chris Angelico wrote: Decorators are fairly straight-forward if you understand higher-order functions. ChrisA I was just minding my own business, and thought to write my first decorator for a simple *recursive* function f. The decorator WORKS if f does not make a call to itself.Otherwise, f seems to have "difficulty" calling itself (I get a typerror, f(n) has value "NoneType"). What is the explanation for this? Does f have a new name because it has a decorator on it now? Note: I am not using functools.wraps since I don't yet understand the reason I might do that yet (first things first... ). Thanks! Bill -- https://mail.python.org/mailman/listinfo/python-list
Re: newb question about @property
Steve D'Aprano wrote: There's no need to set the radius and the diameter, as one is completely derived from the other Good point; I'm glad I submitted my code for grading. Sort of a "trick question" to ask me to add diameter and then take off points because I used it! ; ) Bill and the transformation is cheap enough to perform on the fly as needed. Consider what happens if, due to a bug or an accident, you end up with a Circle instance that says the radius is 5 and the diameter is 20. They can't *both* be right, and -- https://mail.python.org/mailman/listinfo/python-list
Re: newb question about @property
On Tue, 3 Oct 2017 06:32 am, Bill wrote: > Steve D'Aprano wrote: >> Circle didn't use any setters, but I could have let you set the >> diameter, which in >> turn would set the radius: >> >> circle.radius = 2 >> assert circle.diameter == 4 >> circle.diameter == 2 # requires a setter >> assert circle.radius == 1 >> >> Getting that to work is left as an exercise :-) >> > It WAS a good exercise!! I was concerned about "infinite recursion" > between my two property setters.. Thanks! Next? :) > > Bill > > > import math > > > class Circle(object): > """ Define a circle class with radius and diameter""" > def __init__(self, radius): > self.radius = radius > self.diameter =2*radius There's no need to set the radius and the diameter, as one is completely derived from the other and the transformation is cheap enough to perform on the fly as needed. Consider what happens if, due to a bug or an accident, you end up with a Circle instance that says the radius is 5 and the diameter is 20. They can't *both* be right, and you will get inconsistent results depending on whether your other methods happen to use the diameter or the radius. Instead, we should steal the Single Source Of Truth principle from informations systems theory: https://en.wikipedia.org/wiki/Single_source_of_truth https://en.wikipedia.org/wiki/Single_version_of_the_truth There are valid cases for violating this principle in object design, e.g. caches are technically a violation of SSOT but an acceptable one. With that in mind, your class simplifies to: class Circle(object): """ Define a circle class with radius and diameter""" def __init__(self, radius): self.radius = radius @property def radius(self): return self._radius @radius.setter def radius(self, value): self._radius = value @property def diameter(self): return 2*self._radius @diameter.setter def diameter(self, value): self._radius = value/2 @property def area(self): return math.pi*self.radius**2 @property def circumference(self): return math.pi*self.diameter -- Steve “Cheer up,” they said, “things could be worse.” So I cheered up, and sure enough, things got worse. -- https://mail.python.org/mailman/listinfo/python-list
Re: newb question about @property
On Mon, Oct 2, 2017 at 1:32 PM, Billwrote: > @property def circumference(self): > return 2 * math.pi *self.radius Of course the *proper* formula here is just math.tau * self.radius. -- https://mail.python.org/mailman/listinfo/python-list
Re: newb question about @property
On Tue, Oct 3, 2017 at 6:51 AM, Billwrote: > Can you inspire me with a good decorator problem (standard homework > exercise-level will be fine)? Otherwise I will go create one which will > prints a row of asterisks before and after the call to the original function > (which I think I should do). But maybe you have something more interesting? > Or maybe you can refer me to a good source of Python problems, so I can bug > you less? > Start with the row of asterisks. Then change your function to make the "ending" line also say how long the function took to complete. That's a handy tool (albeit a simplistic implementation of it). You may find codewars.com useful, though I don't know how many Python-specific puzzles they have. Or just search the web for "programming puzzles" and see what you find. ChrisA -- https://mail.python.org/mailman/listinfo/python-list
Re: newb question about @property
Chris Angelico wrote: On Tue, Oct 3, 2017 at 6:23 AM, Larry Hudson via Python-listwrote: On 10/01/2017 03:52 PM, Bill wrote: Steve D'Aprano wrote: The definitive explanation of descriptors is here: https://docs.python.org/3/howto/descriptor.html Thank you! It is next on my list. Then I'll try that Circle problem you mentioned as an exercise last night! I don't expect run into any difficulties. : ) Except perhaps for your sense of time... "I'll try" implies the future, "last night" is the past.:-) :-) (Couldn't resist...) Yes, but "you mentioned" implies the past. I think you have an operator precedence issue. Kappa ChrisA Reading the "definitive explanation of descriptors" took me longer than expected.. I am as overly optimistic as any programming person.. ;) Can you inspire me with a good decorator problem (standard homework exercise-level will be fine)? Otherwise I will go create one which will prints a row of asterisks before and after the call to the original function (which I think I should do). But maybe you have something more interesting? Or maybe you can refer me to a good source of Python problems, so I can bug you less? Bill -- https://mail.python.org/mailman/listinfo/python-list
Re: newb question about @property
Steve D'Aprano wrote: Circle didn't use any setters, but I could have let you set the diameter, which in turn would set the radius: circle.radius = 2 assert circle.diameter == 4 circle.diameter == 2 # requires a setter assert circle.radius == 1 Getting that to work is left as an exercise :-) It WAS a good exercise!! I was concerned about "infinite recursion" between my two property setters.. Thanks! Next? :) Bill import math class Circle(object): """ Define a circle class with radius and diameter""" def __init__(self, radius): self.radius = radius self.diameter =2*radius @property def radius(self): return self._radius @radius.setter def radius(self, value): self._radius = value self._diameter=2*value @property def diameter(self): return self._diameter @diameter.setter def diameter(self, value): self._diameter = value self._radius = value /2 @property def area(self): return math.pi *self.radius **2 @property def circumference(self): return 2 * math.pi *self.radius ## Start here ## circle = Circle(1 / math.pi) print("Area = {:.2f}".format(circle.area)) print("Circumference = {:.2f}".format(circle.circumference)) circle.radius =2 assert circle.diameter ==4 print("Area = {:.2f}".format(circle.area)) print("Circumference = {:.2f}".format(circle.circumference)) circle.diameter =2 # requires a setter assert circle.radius ==1 print("Area = {:.2f}".format(circle.area)) print("Circumference = {:.2f}".format(circle.circumference)) -- https://mail.python.org/mailman/listinfo/python-list
Re: newb question about @property
On Tue, Oct 3, 2017 at 6:23 AM, Larry Hudson via Python-listwrote: > On 10/01/2017 03:52 PM, Bill wrote: >> >> Steve D'Aprano wrote: >>> >>> The definitive explanation of descriptors is here: >>> https://docs.python.org/3/howto/descriptor.html >> >> >> Thank you! It is next on my list. Then I'll try that Circle problem you >> mentioned as an exercise last night! I don't expect run into any >> difficulties. : ) >> > > Except perhaps for your sense of time... "I'll try" implies the future, > "last night" is the past.:-) :-) > > (Couldn't resist...) Yes, but "you mentioned" implies the past. I think you have an operator precedence issue. Kappa ChrisA -- https://mail.python.org/mailman/listinfo/python-list
Re: newb question about @property
On 10/01/2017 03:52 PM, Bill wrote: Steve D'Aprano wrote: The definitive explanation of descriptors is here: https://docs.python.org/3/howto/descriptor.html Thank you! It is next on my list. Then I'll try that Circle problem you mentioned as an exercise last night! I don't expect run into any difficulties. : ) Except perhaps for your sense of time... "I'll try" implies the future, "last night" is the past.:-) :-) (Couldn't resist...) -- -=- Larry -=- -- https://mail.python.org/mailman/listinfo/python-list
Re: newb question about @property
On Mon, 2 Oct 2017 07:51 pm, Marko Rauhamaa wrote: > Chris Angelico: > >> On Mon, Oct 2, 2017 at 5:34 PM, Marko Rauhamaa wrote: >>> I have *seen* a semi-useful decorator in code once >>> (@contextlib.contextmanager) but still would prefer explicit dunder >>> methods. >> >> [...] I'm not sure where dunder methods come into this, though, as >> they're completely unrelated. > > A context manager must implement __enter__() and __exit__(). > @contextlib.contextmanager implements them for you. Nobody is holding a gun to your head and forcing you to use @contextmanager. Its a convenience, nothing more. You can still write your own __enter__ and __exit__ methods if you prefer. -- Steve “Cheer up,” they said, “things could be worse.” So I cheered up, and sure enough, things got worse. -- https://mail.python.org/mailman/listinfo/python-list
Re: newb question about @property
On 2017-10-02 10:51, Marko Rauhamaa wrote: > Chris Angelico: > >> On Mon, Oct 2, 2017 at 5:34 PM, Marko Rauhamaa wrote: >>> I have *seen* a semi-useful decorator in code once >>> (@contextlib.contextmanager) but still would prefer explicit dunder >>> methods. >> >> [...] I'm not sure where dunder methods come into this, though, as >> they're completely unrelated. > > A context manager must implement __enter__() and __exit__(). > @contextlib.contextmanager implements them for you. Let's revisit the bit of Chris' mail you didn't quote: On 2017-10-02 09:02, Chris Angelico wrote: > > There are plenty of programs that don't need decorators, but in some > contexts, they are just beautiful. Building a web app in Flask or > Django involves functions that get decorated to say what endpoints > they handle, for instance. The point is that there are plenty of useful decorators that have nothing to do with dunder methods. -- Thomas Jollans -- https://mail.python.org/mailman/listinfo/python-list
Re: newb question about @property
Chris Angelico: > On Mon, Oct 2, 2017 at 5:34 PM, Marko Rauhamaa wrote: >> I have *seen* a semi-useful decorator in code once >> (@contextlib.contextmanager) but still would prefer explicit dunder >> methods. > > [...] I'm not sure where dunder methods come into this, though, as > they're completely unrelated. A context manager must implement __enter__() and __exit__(). @contextlib.contextmanager implements them for you. https://docs.python.org/3/library/contextlib.html#contextlib.cont extmanager> Marko -- https://mail.python.org/mailman/listinfo/python-list
Re: newb question about @property
On Mon, 2 Oct 2017 05:34 pm, Marko Rauhamaa wrote: > I must say, though, I have yet to run into a need for descriptors. You've never called a method? -- Steve “Cheer up,” they said, “things could be worse.” So I cheered up, and sure enough, things got worse. -- https://mail.python.org/mailman/listinfo/python-list
Re: newb question about @property
On Mon, Oct 2, 2017 at 5:34 PM, Marko Rauhamaawrote: > Chris Angelico : > >> Yes, that's correct. The *descriptor* protocol is what allows >> "foo.bar" to cause a function to be executed > > That mechanism allows you to expose data fields in the API. If the > implementation later changes, you can emulate the data fields. > > I must say, though, I have yet to run into a need for descriptors. The beauty of Python (over, say, C++) is that you can transparently convert something from being a simple data attribute to being a descriptor. The mere fact that they *exist* benefits your code; they're like a safety net that lets you do what makes sense without worrying that someday, maybe, one of these things might have to become a function. In C++, if something might ever need to be a function, it has to be a function *now*, so Best Practice is to write getters and setters for everything, just in case. In Python, you can convert it to use @property if you ever actually need to, which means you do nothing now. >> the *decorator* protocol is what lets you "tag" a function: > > i have yet to need that, either. I have *seen* a semi-useful decorator > in code once (@contextlib.contextmanager) but still would prefer > explicit dunder methods. There are plenty of programs that don't need decorators, but in some contexts, they are just beautiful. Building a web app in Flask or Django involves functions that get decorated to say what endpoints they handle, for instance. I've periodically used a simple decorator plus some info in the function's docstring to do more magic. I'm not sure where dunder methods come into this, though, as they're completely unrelated. ChrisA -- https://mail.python.org/mailman/listinfo/python-list
Re: newb question about @property
Chris Angelico: > Yes, that's correct. The *descriptor* protocol is what allows > "foo.bar" to cause a function to be executed That mechanism allows you to expose data fields in the API. If the implementation later changes, you can emulate the data fields. I must say, though, I have yet to run into a need for descriptors. > the *decorator* protocol is what lets you "tag" a function: i have yet to need that, either. I have *seen* a semi-useful decorator in code once (@contextlib.contextmanager) but still would prefer explicit dunder methods. Marko -- https://mail.python.org/mailman/listinfo/python-list
Re: newb question about @property
On Sunday, October 1, 2017 at 6:47:34 PM UTC+1, MRAB wrote: > On 2017-10-01 02:52, Stefan Ram wrote: > > MRAB writes: > >>raise ValueError("Temperature below -273 is not possible") > > > >-273.15 > > > I think you've trimmed a little too much. In my reply I was only copying > what someone else had written. At least it doesn't contain the bloody irritating » and « which drive my autistic head right up the wall. -- Kindest regards. Mark Lawrence. -- https://mail.python.org/mailman/listinfo/python-list
Re: newb question about @property
On Mon, Oct 2, 2017 at 9:47 AM, Billwrote: > Stephan Houben wrote: >> >> Op 2017-10-01, Bill schreef : >>> >>> I watched an example on YouTube where someone wrote a simple descriptor >>> ("@Time_it) to output the amount of time that it took ordinary functions >>> to complete.To be honest, I AM interested in descriptors. >> >> Are you sure you are not confusing deSCRIPTtors and deCORAtors here? > > > Yet, you are absolutely correct! Thank you for clarifying! From your > description, I can see that it was *decorators*, which drew my interest. It > appears that *property* is perhaps both a decorator and a descriptor, at > least when used as in the examples we have been discussing. Yes, that's correct. The *descriptor* protocol is what allows "foo.bar" to cause a function to be executed; the *decorator* protocol is what lets you "tag" a function: class Foo: @property # this is being used as a decorator def bar(self): return 42 foo = Foo(); print(foo.bar) # this uses descriptor protocol to find the function Decorators are fairly straight-forward if you understand higher-order functions. If you DON'T understand higher-order functions, look those up first and get a solid understanding of what it means to pass a function as a parameter to another function. ChrisA -- https://mail.python.org/mailman/listinfo/python-list
Re: newb question about @property
Steve D'Aprano wrote: The definitive explanation of descriptors is here: https://docs.python.org/3/howto/descriptor.html Thank you! It is next on my list. Then I'll try that Circle problem you mentioned as an exercise last night! I don't expect run into any difficulties. : ) -- https://mail.python.org/mailman/listinfo/python-list
Re: newb question about @property
Stephan Houben wrote: Op 2017-10-01, Bill schreef: I watched an example on YouTube where someone wrote a simple descriptor ("@Time_it) to output the amount of time that it took ordinary functions to complete.To be honest, I AM interested in descriptors. Are you sure you are not confusing deSCRIPTtors and deCORAtors here? Yet, you are absolutely correct! Thank you for clarifying! From your description, I can see that it was *decorators*, which drew my interest. It appears that *property* is perhaps both a decorator and a descriptor, at least when used as in the examples we have been discussing. According to the language grammar, which all newbys like me should have handy (remember my ADT acronym from another thread?), decorators can be applied to a classdef, a funcdef, or a async_funcdef (the latter I assume is a "callback" function definition). Surely the difference in syntax between funcdef and async_funcdef will be revealed to me by looking closer at the grammar! : ) Bill @Time_it is decorator syntax. Despite the similarity in the words, there are totally different things. Descriptors are objects with __get__, and optionally __set__ and __delete__ methods (i.e. they implement the descriptor protocols). Decorators aren't really an official type, but loosely speaking these are any functions which can be applied meaningfully with a single function or class as argument. Some very mundane functions can be (ab)used as decorators. In [1]: @repr ...: def hello(): ...: pass ...: In [2]: hello Out[2]: '' Stephan -- https://mail.python.org/mailman/listinfo/python-list
Re: newb question about @property
Steve D'Aprano wrote: On Sun, 1 Oct 2017 05:46 pm, Bill wrote: If you were going to show non-Python users, say science undergraduates and faculty, that Python is an interesting tool (in 45 minutes), would one delve into descriptors? Hell no :-) Oops, I see I used the word "descriptor", where I meant "decorator" (at least newb is still in the subject line). I don't even know what a descriptor is yet--I know a bit more about meta-classes! %-) So, on your list, I'm basically up to the leading edge of (5), writing decorators. But my previous programming experience helped me to blast through (1) to (4). Going forward, it appears the walkway will be a little steeper. I do appreciate your list as it improves my perspective. From the point of view of getting others to be interested, I'm not sure *classes* and object oriented design/thinking do it. I think they are more of an acquired taste... Functions, on the other hand, are easy to like, I think--especially in Python. I think there's a hierarchy of difficulty/complexity/mind-bogglingness in Python. From least complex to most: - Using Python in an imperative fashion, as in simple scripts. - Writing your own functions. - Writing your own classes. - Writing generators. - Using decorators, including property. - Writing your own decorators. - Writing your own descriptors. - Writing your own metaclasses (a.k.a. "the killer joke"). I wouldn't touch the last three in a beginner's class, not unless they already had a significant amount of programming experience. I am thinking maybe. Here is what I am thinking at this moment: trivial applications (probably), list comprehensions (definitely), generators (maybe briefly). Whatever I would discuss, I think ending with descriptors could be a strong finish. That depends on whether your aim is to confuse them or not :-) I don't think the descriptor protocol is something you'll be able to explain in five or ten minutes. *Using* descriptors like property, sure, that's fine. But I'm certainly not merely interested for the sake of my talk, I obtain some satisfaction in learning how things work. If you can suggest any references for descriptors which you think are good, I would be interested. The definitive explanation of descriptors is here: https://docs.python.org/3/howto/descriptor.html Thanks! Bill -- https://mail.python.org/mailman/listinfo/python-list
Re: newb question about @property
Op 2017-10-01, Bill schreef: > Steve D'Aprano wrote: >> >> [1] Technically, the interpreter knows nothing about properties. What >> it cares about is *descriptors*. Properties are just one kind of >> descriptor, as are methods. But I'm intentionally not talking about >> the gory details of descriptors. Feel free to ask if you care, but >> honestly, you don't need to care unless you are writing your own >> descriptor class. >> > I found the following page to be a very easily accessible discussion > about descriptors (it represents the state of my knowledge about > descriptors). You got more? : ) > > https://www.programiz.com/python-programming/decorator I found the page to be a discussion about decorators (unsurprisingly given the URL) and not containing the word "descriptor" at all... Note that the remark from Steve is on the topic of descriptors. I suppose the first advice to anybody wanting to learn about either descriptors or decorators is to not confuse them with the other thing. Stephan -- https://mail.python.org/mailman/listinfo/python-list
Re: newb question about @property
Op 2017-10-01, Bill schreef: > I watched an example on YouTube where someone wrote a simple descriptor > ("@Time_it) to output the amount of time that it took ordinary functions > to complete.To be honest, I AM interested in descriptors. Are you sure you are not confusing deSCRIPTtors and deCORAtors here? @Time_it is decorator syntax. Despite the similarity in the words, there are totally different things. Descriptors are objects with __get__, and optionally __set__ and __delete__ methods (i.e. they implement the descriptor protocols). Decorators aren't really an official type, but loosely speaking these are any functions which can be applied meaningfully with a single function or class as argument. Some very mundane functions can be (ab)used as decorators. In [1]: @repr ...: def hello(): ...: pass ...: In [2]: hello Out[2]: '' Stephan -- https://mail.python.org/mailman/listinfo/python-list
Re: newb question about @property
On 2017-10-01 02:52, Stefan Ram wrote: MRABwrites: raise ValueError("Temperature below -273 is not possible") -273.15 I think you've trimmed a little too much. In my reply I was only copying what someone else had written. -- https://mail.python.org/mailman/listinfo/python-list
Re: newb question about @property
On 01/10/17 03:52, Stefan Ram wrote: > MRABwrites: >> raise ValueError("Temperature below -273 is not possible") > -273.15 > Either way, that depends. https://en.wikipedia.org/wiki/Negative_temperature#Examples -- https://mail.python.org/mailman/listinfo/python-list
Re: newb question about @property
On Sun, 1 Oct 2017 05:46 pm, Bill wrote: > If you were going to show non-Python users, say science undergraduates > and faculty, that Python is an interesting tool (in 45 minutes), would > one delve into descriptors? Hell no :-) I think there's a hierarchy of difficulty/complexity/mind-bogglingness in Python. From least complex to most: - Using Python in an imperative fashion, as in simple scripts. - Writing your own functions. - Writing your own classes. - Writing generators. - Using decorators, including property. - Writing your own decorators. - Writing your own descriptors. - Writing your own metaclasses (a.k.a. "the killer joke"). I wouldn't touch the last three in a beginner's class, not unless they already had a significant amount of programming experience. > I am thinking maybe. Here is what I am > thinking at this moment: trivial applications (probably), list > comprehensions (definitely), generators (maybe briefly). Whatever I > would discuss, I think ending with descriptors could be a strong finish. That depends on whether your aim is to confuse them or not :-) I don't think the descriptor protocol is something you'll be able to explain in five or ten minutes. *Using* descriptors like property, sure, that's fine. > But I'm certainly not merely interested for the sake of my talk, I > obtain some satisfaction in learning how things work. If you can > suggest any references for descriptors which you think are good, I would > be interested. The definitive explanation of descriptors is here: https://docs.python.org/3/howto/descriptor.html -- Steve “Cheer up,” they said, “things could be worse.” So I cheered up, and sure enough, things got worse. -- https://mail.python.org/mailman/listinfo/python-list
Re: newb question about @property
Steve D'Aprano wrote: [1] Technically, the interpreter knows nothing about properties. What it cares about is *descriptors*. Properties are just one kind of descriptor, as are methods. But I'm intentionally not talking about the gory details of descriptors. Feel free to ask if you care, but honestly, you don't need to care unless you are writing your own descriptor class. I found the following page to be a very easily accessible discussion about descriptors (it represents the state of my knowledge about descriptors). You got more? : ) https://www.programiz.com/python-programming/decorator -- https://mail.python.org/mailman/listinfo/python-list
Re: newb question about @property
Steve D'Aprano wrote: Circle didn't use any setters, but I could have let you set the diameter, which in turn would set the radius: circle.radius = 2 assert circle.diameter == 4 circle.diameter == 2 # requires a setter assert circle.radius == 1 Getting that to work is left as an exercise :-) I may start that exercise in a few minutes! But most commonly, computed attributes need to store some data aside, somewhere. You could use a global variable, or write it to a file, or stick it in a list. All of these things have obvious problems, so the most sensible approach it to stick the data in a private attribute. The interpreter doesn't enforce notions of private/public when it comes to Python classes, but there's a very strong convention that anything starting with a single underscore is private. [1] Technically, the interpreter knows nothing about properties. What it cares about is *descriptors*. Properties are just one kind of descriptor, as are methods. But I'm intentionally not talking about the gory details of descriptors. Feel free to ask if you care, but honestly, you don't need to care unless you are writing your own descriptor class. Thank you, and everyone else who has contributed to this thread, for helping me. Each contribution I read helped me to get further ahead! I watched an example on YouTube where someone wrote a simple descriptor ("@Time_it) to output the amount of time that it took ordinary functions to complete.To be honest, I AM interested in descriptors. I may reexamine whether David Beazley has more to say about them in his book "Python: Essential Reference", which I have been reading. Obviously, I still have some gaps in my understanding after my first reading. If you were going to show non-Python users, say science undergraduates and faculty, that Python is an interesting tool (in 45 minutes), would one delve into descriptors? I am thinking maybe. Here is what I am thinking at this moment: trivial applications (probably), list comprehensions (definitely), generators (maybe briefly). Whatever I would discuss, I think ending with descriptors could be a strong finish. But I'm certainly not merely interested for the sake of my talk, I obtain some satisfaction in learning how things work. If you can suggest any references for descriptors which you think are good, I would be interested. Thanks, Bill -- https://mail.python.org/mailman/listinfo/python-list
Re: newb question about @property
On Sun, 1 Oct 2017 11:07 am, Bill wrote: > You and Ned are both right. Up until a few minutes ago, I wasn't > thinking about a class having more than 1 attribute that I wanted to > change. And now I realize that __get__ doesn't really make sense in > that context (in the back of my mind was the notion that @property > defined __get__, __set__ and __del__) and I allowed that to obscure my > vision. I was on the verge of giving up anything to do with computers, > forever. : ) property *does* define __get__ and __set__, but not __del__, rather it is called __delete__. Confusing, I know, but neither __del__ nor __delete__ should be used very often. __del__ is the instance destructor. When you have an object: obj = Some_Class() and the last reference to that object goes out of scope, the garbage collector collects the object and reclaims its memory. Before doing so, it calls obj.__del__ if it exists. (That's a simplified version.) __delete__ on the other hand is part of the descriptor protocol, which is how Python manages: - methods - classmethod and staticmethod - property and more. Generally speaking, the descriptor protocol is considered "advanced", not as advanced or scary as metaclasses, but not for beginners either. However, using property is much less complex. Properties are *computed attributes*. Here is a grossly simplified explanation of how Python does attribute look-ups, closer to what Python did in version 1.5 than what it does now, but its a good place to start. There are three things you can so with an attribute: get it, delete it, or set it. (A) When you try getting an attribute: result = obj.spam the interpreter starts by looking in the object's instance namespace for a key 'spam': obj.__dict__['spam'] If it finds it, it returns the associated value and we're done. If not, it next looks at the object's class: obj.__class__.__dict__['spam'] and if not, then it looks in any superclasses, then it looks for a __getattr__ method, and finally if all else fails it raises AttributeError. (B) Deleting an attribute: del obj.spam is pretty much the same. (C) When you try setting an attribute: obj.spam = eggs the interpreter assigns to the instance namespace: obj.__class__.__dict__['spam'] = eggs So that's (roughly) how Python worked back in 1998 or so, and its still conceptually close to what happens now. Now let's introduce an extra layer of complexity: properties[1]. (A) if the result of looking up obj.spam is a property object, then instead of returning the property object itself, the interpreter calls the property's getter method, and returns what it returns. (B) Likewise, deleting the property calls the property's deleter method. (Its rare to bother with one of them, so in practice you can ignore it.) (C) And setting obj.spam to a new value calls the property's setter method, which is intended to handle setting the value somewhere. (Again, I'm ignoring much of the complexity needed to by the actual implementation, in order to focus on just the basic conceptual steps.) > BTW, your example (below) is very nice! I may have seen something > similar before, but I am starting to appreciate it better now. I think > all of this would have made a bit more sense (to me), if instead of just > "@property", the syntax was "@property.getter". One of the most common use for properties is read-only attributes, so the decision was made long ago to have property alone to be equivalent to property.getter. But if you feel the need, you can write: @property.getter def spam(self): ... but if you do, expect people to look at you funny and say "what's that do?". > Now I am forced to ask > the question, why did they use the underscore (on temperature) in the > example on the bottom of this page? Is one forced to introduce new > identifiers in order to define a setter? Forced to? Not exactly. A computed attribute need not have any storage at all, or it could only use public attributes, like my earlier Circle example. Circle didn't use any setters, but I could have let you set the diameter, which in turn would set the radius: circle.radius = 2 assert circle.diameter == 4 circle.diameter == 2 # requires a setter assert circle.radius == 1 Getting that to work is left as an exercise :-) But most commonly, computed attributes need to store some data aside, somewhere. You could use a global variable, or write it to a file, or stick it in a list. All of these things have obvious problems, so the most sensible approach it to stick the data in a private attribute. The interpreter doesn't enforce notions of private/public when it comes to Python classes, but there's a very strong convention that anything starting with a single underscore is private. [1] Technically, the interpreter knows nothing about properties. What it cares about is *descriptors*. Properties are just one kind of descriptor, as are methods. But I'm intentionally not talking
Re: newb question about @property
On 30Sep2017 20:07, Billwrote: think all of this would have made a bit more sense (to me), if instead of just "@property", the syntax was "@property.getter". Perhaps, but nobody wants to type this. Also many properties are ready only, so that is the default. Now I am forced to ask the question, why did they use the underscore (on temperature) in the example on the bottom of this page? Is one forced to introduce new identifiers in order to define a setter? https://www.programiz.com/python-programming/property class Celsius: def __init__(self, temperature = 0): self._temperature = temperature [...snip...] @property def temperature(self): print("Getting value") return self._temperature @temperature.setter def temperature(self, value): if value < -273: raise ValueError("Temperat print("Setting value") self._temperature = value because the name self.temperature is taken by the property, one must store underlying values in a different name. Since the property is one to one with the actual internal value here and they're just using the setter protery to do a sanity check, they named the internal value very similarly. By using "_temperature" they (a) keep the name very similar and (b) make it clear that the internal value is "private", not intended for direct use by code outside the class. Cheers, Cameron Simpson (formerly c...@zip.com.au) -- https://mail.python.org/mailman/listinfo/python-list
Re: newb question about @property
On 2017-10-01 01:07, Bill wrote: Steve D'Aprano wrote: On Sun, 1 Oct 2017 08:47 am, Bill wrote: I spent a few hours experimenting with @property. To my mind it seems like it would be preferable to just define (override) instance methods __get__(), __set__(), and possibly __del__(), as desired, as I could easily provide them with "ideal" customization. Am I overlooking something? Probably. You and Ned are both right. Up until a few minutes ago, I wasn't thinking about a class having more than 1 attribute that I wanted to change. And now I realize that __get__ doesn't really make sense in that context (in the back of my mind was the notion that @property defined __get__, __set__ and __del__) and I allowed that to obscure my vision. I was on the verge of giving up anything to do with computers, forever. : ) BTW, your example (below) is very nice! I may have seen something similar before, but I am starting to appreciate it better now. I think all of this would have made a bit more sense (to me), if instead of just "@property", the syntax was "@property.getter". Now I am forced to ask the question, why did they use the underscore (on temperature) in the example on the bottom of this page? Is one forced to introduce new identifiers in order to define a setter? https://www.programiz.com/python-programming/property self._temperature is where the value is actually stored. Suppose you had this instead: @temperature.setter def temperature(self, value): if value < -273: raise ValueError("Temperature below -273 is not possible") print("Setting value") self.temperature = value # <-- changed this line What would happen when you tried to set the temperature? Because of the last line, the setter would be calling itself recursively until it hit the maximum stack depth. A leading underscore is the normal convention to indicate that it should be treated as "private". If you wanted the temperature to be read-only, you'd make the value "private" and have a getter but not a setter. [snip] -- https://mail.python.org/mailman/listinfo/python-list
Re: newb question about @property
Ned Batchelder wrote: On 9/30/17 7:18 PM, Bill wrote: Ned Batchelder wrote: On 9/30/17 5:47 PM, Bill wrote: I spent a few hours experimenting with @property. To my mind it seems like it would be preferable to just define (override) instance methods __get__(), __set__(), and possibly __del__(), as desired, as I could easily provide them with "ideal" customization. Am I overlooking something? It would be easier to comment if you showed the two options. One with @property, and one with __get__ etc. A downside to __get__ is that you need to create a class with those methods, and then instantiate that class as an attribute in your real class, whereas @property can be used without a lot of rigamarole. --Ned. I am basically mimmicking what I see at (the very bottom of) this page: https://www.programiz.com/python-programming/property Can you show us the code you are using it to mimic that? --Ned. Here it is, Ned. It's my first attempt at using classes in Python. I still have to learn how to incorporate datetime appropriately! :) import datetime # object oriented example class Employee(object): ''' This class will abstract an employee. Class date members name, a string birthday, a date object address, a string position It also has a static data member for the number of employees. ''' num_employees =0 # class data item @classmethod def get_num_employees(cls): return Employee.num_employees def __init__(self, name, birthdate, address, position): Employee.num_employees +=1 self.name = name self.birthdate = birthdate self.address = address self.position = position @property def address(self): print("**Hi from address-getter**") return self._address @address.setter def address(self, value): print("*Hi, from address setter()!") self._address = value def __del__(self): print("*** Hi, from __del__()") ##Employee.num_employees -= 1 def __str__(self): return 'Name: {}, Born: {} \nAddress: {} \nPosition: {} \n'.\ format(self.name,self.birthdate,self.address,self.position) class SalesPerson(Employee): def __init__(self, name, bdate, addr): super().__init__(name, bdate, addr,"Salesperson") def main(): emp1 = Employee("Sam","4/30/1970","212 Elm","Programmer") emp2 = SalesPerson("Gene","5/1/79","414 Maple") ## Note: learn to use datetime.date--> str print(emp1) print(emp2) emp1.address ="230 Main Street" # call setter? print(emp1) del(emp1) print("Number of employees", Employee.get_num_employees()) print('*'*30) main()#call main() -- https://mail.python.org/mailman/listinfo/python-list
Re: newb question about @property
Steve D'Aprano wrote: On Sun, 1 Oct 2017 08:47 am, Bill wrote: I spent a few hours experimenting with @property. To my mind it seems like it would be preferable to just define (override) instance methods __get__(), __set__(), and possibly __del__(), as desired, as I could easily provide them with "ideal" customization. Am I overlooking something? Probably. You and Ned are both right. Up until a few minutes ago, I wasn't thinking about a class having more than 1 attribute that I wanted to change. And now I realize that __get__ doesn't really make sense in that context (in the back of my mind was the notion that @property defined __get__, __set__ and __del__) and I allowed that to obscure my vision. I was on the verge of giving up anything to do with computers, forever. : ) BTW, your example (below) is very nice! I may have seen something similar before, but I am starting to appreciate it better now. I think all of this would have made a bit more sense (to me), if instead of just "@property", the syntax was "@property.getter". Now I am forced to ask the question, why did they use the underscore (on temperature) in the example on the bottom of this page? Is one forced to introduce new identifiers in order to define a setter? https://www.programiz.com/python-programming/property Thanks! -Bill This is a particularly simple example, with only getters. How would you write it by overriding __get__? class Circle(object): def __init__(self, centre, radius): self.centre = centre self.radius = radius @property def diameter(self): return 2*self.radius @property def area(self): return pi*self.radius**2 @property def circumference(self): return pi*self.diameter -- https://mail.python.org/mailman/listinfo/python-list
Re: newb question about @property
On 9/30/17 7:18 PM, Bill wrote: Ned Batchelder wrote: On 9/30/17 5:47 PM, Bill wrote: I spent a few hours experimenting with @property. To my mind it seems like it would be preferable to just define (override) instance methods __get__(), __set__(), and possibly __del__(), as desired, as I could easily provide them with "ideal" customization. Am I overlooking something? It would be easier to comment if you showed the two options. One with @property, and one with __get__ etc. A downside to __get__ is that you need to create a class with those methods, and then instantiate that class as an attribute in your real class, whereas @property can be used without a lot of rigamarole. --Ned. I am basically mimmicking what I see at (the very bottom of) this page: https://www.programiz.com/python-programming/property Can you show us the code you are using it to mimic that? --Ned. -- https://mail.python.org/mailman/listinfo/python-list
Re: newb question about @property
On Sun, 1 Oct 2017 08:47 am, Bill wrote: > I spent a few hours experimenting with @property. To my mind it seems > like it would be preferable to just define (override) instance methods > __get__(), __set__(), and possibly __del__(), as desired, as I could > easily provide them with "ideal" customization. Am I overlooking something? Probably. This is a particularly simple example, with only getters. How would you write it by overriding __get__? class Circle(object): def __init__(self, centre, radius): self.centre = centre self.radius = radius @property def diameter(self): return 2*self.radius @property def area(self): return pi*self.radius**2 @property def circumference(self): return pi*self.diameter -- Steve “Cheer up,” they said, “things could be worse.” So I cheered up, and sure enough, things got worse. -- https://mail.python.org/mailman/listinfo/python-list
Re: newb question about @property
On 9/30/17 5:47 PM, Bill wrote: I spent a few hours experimenting with @property. To my mind it seems like it would be preferable to just define (override) instance methods __get__(), __set__(), and possibly __del__(), as desired, as I could easily provide them with "ideal" customization. Am I overlooking something? It would be easier to comment if you showed the two options. One with @property, and one with __get__ etc. A downside to __get__ is that you need to create a class with those methods, and then instantiate that class as an attribute in your real class, whereas @property can be used without a lot of rigamarole. --Ned. -- https://mail.python.org/mailman/listinfo/python-list
newb question about @property
I spent a few hours experimenting with @property. To my mind it seems like it would be preferable to just define (override) instance methods __get__(), __set__(), and possibly __del__(), as desired, as I could easily provide them with "ideal" customization. Am I overlooking something? Bill -- https://mail.python.org/mailman/listinfo/python-list