Re: newb question about @property

2017-10-06 Thread bartc

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

2017-10-05 Thread Steve D'Aprano
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

2017-10-05 Thread bartc

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

2017-10-05 Thread Gregory Ewing

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

2017-10-05 Thread Steve D'Aprano
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]

2017-10-04 Thread Steve D'Aprano
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

2017-10-04 Thread Steve D'Aprano
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

2017-10-04 Thread bartc

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

2017-10-04 Thread ROGER GRAYDON CHRISTMAN

On Wed, Oct  4, 2017 14:03 PM, bartc  wrote >
"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]

2017-10-04 Thread Paul Moore
On 4 October 2017 at 17:15, Ian Kelly  wrote:
> 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

2017-10-04 Thread Paul Moore
On 4 October 2017 at 17:02, Rhodri James  wrote:
> 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]

2017-10-04 Thread Ian Kelly
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
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: newb question about @property

2017-10-04 Thread Rhodri James

On 04/10/17 16:33, Paul Moore wrote:

On 4 October 2017 at 16:03, bartc  wrote:

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

2017-10-04 Thread Paul Moore
On 4 October 2017 at 16:03, bartc  wrote:
> 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]

2017-10-04 Thread Steve D'Aprano
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

2017-10-04 Thread bartc

On 04/10/2017 14:41, Ian Kelly wrote:

On Wed, Oct 4, 2017 at 5:07 AM, bartc  wrote:



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

2017-10-04 Thread Ian Kelly
On Wed, Oct 4, 2017 at 5:07 AM, bartc  wrote:
> 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

2017-10-04 Thread Rhodri James

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

2017-10-04 Thread bartc

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

2017-10-03 Thread Steve D'Aprano
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

2017-10-03 Thread Ian Kelly
On Tue, Oct 3, 2017 at 9:16 AM, Jorge Gimeno  wrote:
> 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

2017-10-03 Thread Bill

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

2017-10-03 Thread Jorge Gimeno
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, bartc  wrote:

> 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

2017-10-03 Thread bartc

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


Re: newb question about @property

2017-10-03 Thread Ian Kelly
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.

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

2017-10-03 Thread Steve D'Aprano
On Tue, 3 Oct 2017 10:01 pm, Lele Gaifax wrote:

> Steve D'Aprano  writes:
> 
>> (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

2017-10-03 Thread Lele Gaifax
Steve D'Aprano  writes:

> (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

2017-10-03 Thread Steve D'Aprano
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

2017-10-03 Thread Bill

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

2017-10-02 Thread Chris Angelico
On Tue, Oct 3, 2017 at 2:39 PM, 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.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

2017-10-02 Thread Bill

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

2017-10-02 Thread Bill

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

2017-10-02 Thread Steve D'Aprano
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

2017-10-02 Thread Ian Kelly
On Mon, Oct 2, 2017 at 1:32 PM, Bill  wrote:
> @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

2017-10-02 Thread Chris Angelico
On Tue, Oct 3, 2017 at 6:51 AM, Bill  wrote:
> 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

2017-10-02 Thread Bill

Chris Angelico wrote:

On Tue, Oct 3, 2017 at 6:23 AM, Larry Hudson via Python-list
 wrote:

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

2017-10-02 Thread Bill

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

2017-10-02 Thread Chris Angelico
On Tue, Oct 3, 2017 at 6:23 AM, Larry Hudson via Python-list
 wrote:
> 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

2017-10-02 Thread Larry Hudson via Python-list

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

2017-10-02 Thread Steve D'Aprano
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

2017-10-02 Thread Thomas Jollans
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

2017-10-02 Thread Marko Rauhamaa
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

2017-10-02 Thread Steve D'Aprano
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

2017-10-02 Thread Chris Angelico
On Mon, Oct 2, 2017 at 5:34 PM, Marko Rauhamaa  wrote:
> 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

2017-10-02 Thread Marko Rauhamaa
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

2017-10-01 Thread breamoreboy
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

2017-10-01 Thread Chris Angelico
On Mon, Oct 2, 2017 at 9:47 AM, Bill  wrote:
> 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

2017-10-01 Thread Bill

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

2017-10-01 Thread Bill

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

2017-10-01 Thread Bill

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

2017-10-01 Thread Stephan Houben
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

2017-10-01 Thread Stephan Houben
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

2017-10-01 Thread MRAB

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.

--
https://mail.python.org/mailman/listinfo/python-list


Re: newb question about @property

2017-10-01 Thread Thomas Jollans
On 01/10/17 03:52, Stefan Ram wrote:
> MRAB  writes:
>> 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

2017-10-01 Thread Steve D'Aprano
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

2017-10-01 Thread Bill

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

2017-10-01 Thread Bill

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

2017-09-30 Thread Steve D'Aprano
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

2017-09-30 Thread Cameron Simpson

On 30Sep2017 20:07, Bill  wrote:
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

2017-09-30 Thread MRAB

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

2017-09-30 Thread Bill

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

2017-09-30 Thread Bill

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

2017-09-30 Thread Ned Batchelder

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

2017-09-30 Thread Steve D'Aprano
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

2017-09-30 Thread Ned Batchelder

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

2017-09-30 Thread Bill
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