JavaScript-Style Object Creation in Python (using a constructor function instead of a class to create objects)
Hi, do you have an opinion on the following? Wouldn't it be nice to define classes via a simple constructor function (as below) instead of a conventional class definition? *conventional*: class MyClass(ParentClass): def __init__(x): self._x = x def my_method(y): z = self._x + y return z *proposed*: def MyClass(x): self = ParentClass() def my_method(y): z = x + y return z self.my_method = my_method # that's cumbersome (see comments below) return self Here are the pros and cons I could come up with for the proposed method: (+) Simpler and more explicit. (+) No need to create attributes (like `self._x`) just to pass something from `__init__` to another method. (+) Default arguments / annotations for methods could be different for each class instance. Adaptive defaults wouldn't have to simulated with a None. (+) Class/instance level imports would work. (-/+) Speed: The `def`-based objects take 0.6 μs to create while the `class`-based objects take only 0.4 μs. For method execution however the closure takes only 0.15 μs while the proper method takes 0.22 μs (script <https://gist.github.com/rmst/78b2b0f56a3d9ec13b1ec6f3bd50aa9c>). (-/+) Checking types: In the proposed example above the returned object wouldn't know that it has been created by `MyClass`. There are a couple of solutions to that, though. The easiest to implement would be to change the first line to `self = subclass(ParentClass())` where the subclass function looks at the next item in the call stack (i.e. `MyClass`) and makes it the type of the object. Another solution would be to have a special rule for functions with capital first letter returning a single object to append itself to the list of types of the returned object. Alternatively there could be a special keyword e.g. `classdef` that would be used instead of `def` if we wouldn't want to rely on the name. (-) The current syntax for adding a function to an object is cumbersome. That's what is preventing me from actually using the proposed pattern. But is this really the only reason for not using it? And if so, wouldn't that be a good argument for enabling something like below? *attribute function definitions*: def MyClass(x): self = ParentClass() def self.my_method(y): z = x + y return z return self or alternatively *multiline lambdas*: def MyClass(x): self = ParentClass() self.my_method = (y): z = x + y return z return self Cheers, Simon
On 2017-05-13 21:07, Simon Ramstedt wrote:
Hi, do you have an opinion on the following?
My general opinion is that imitating JavaScript is almost always a bad idea. :-)
Wouldn't it be nice to define classes via a simple constructor function (as below) instead of a conventional class definition?
*conventional*: | classMyClass(ParentClass): def__init__(x): self._x=x defmy_method(y): z=self._x+y returnz |
*proposed*:
| defMyClass(x): self=ParentClass() defmy_method(y): z=x+y returnz self.my_method=my_method # that's cumbersome (see comments below) returnself |
Here are the pros and cons I could come up with for the proposed method:
(+) Simpler and more explicit.
I don't really see how that's simpler or more explicit. In one respect it's clearly less explicit, as the "self" is implicit.
(+) No need to create attributes (like `self._x`) just to pass something from `__init__` to another method.
Attributes aren't just for passing things to other methods. They're for storing state. In your proposed system, how would an object mutate one of its own attributes? It looks like "x" here is just stored in a function closure, which wouldn't allow easy mutation. Also, how would another object access the attribute from outside (as we currently do with self.x)? You can say we'd only use this new attribute-free approach when we want to pass a constructor argument that's used but never mutated or accessed from outside, but that severely restricts the potential use cases, and all it saves you is typing "self". Relatedly, how is ParentClass itself defined? I don't see how you could bootstrap this without having a real class at the bottom of it somehow (as your test script in fact does).
(+) Default arguments / annotations for methods could be different for each class instance. Adaptive defaults wouldn't have to simulated with a None.
That seems as likely to be a negative as a positive. Having different instances with different default values could be confusing. This would even allow different instances to define totally different methods (with if-logic inside the function constructor), which would definitely be confusing.
(+) Class/instance level imports would work.
How often is that really needed?
(-/+) Speed: The `def`-based objects take 0.6 μs to create while the `class`-based objects take only 0.4 μs. For method execution however the closure takes only 0.15 μs while the proper method takes 0.22 μs (script <https://gist.github.com/rmst/78b2b0f56a3d9ec13b1ec6f3bd50aa9c>).
I don't think you can really evaluate the performance impact of this alternative just based on a trivial example like that.
(-/+) Checking types: In the proposed example above the returned object wouldn't know that it has been created by `MyClass`. There are a couple of solutions to that, though. The easiest to implement would be to change the first line to `self = subclass(ParentClass())` where the subclass function looks at the next item in the call stack (i.e. `MyClass`) and makes it the type of the object. Another solution would be to have a special rule for functions with capital first letter returning a single object to append itself to the list of types of the returned object. Alternatively there could be a special keyword e.g. `classdef` that would be used instead of `def` if we wouldn't want to rely on the name.
Those special rules sound very hackish to me.
(-) The current syntax for adding a function to an object is cumbersome. That's what is preventing me from actually using the proposed pattern. But is this really the only reason for not using it? And if so, wouldn't that be a good argument for enabling something like below? * * *attribute function definitions*: | defMyClass(x): self=ParentClass() defself.my_method(y): z=x+y returnz returnself |
or alternatively*multiline lambdas*:
| defMyClass(x): self=ParentClass() self.my_method=(y): z=x+y returnz returnself |
To be honest, from all your examples, I don't really see what the point is. It's a different syntax for doing some of the same things the existing class syntax does, while providing no apparent way to do some important things (like mutating attributes). I think Python's existing class syntax is simple, clear, and quite nice overall, and creating class instances by calling a function instead of a class doesn't add anything. In fact, even JavaScript has recently added classes to allow programmers to move away from the old approach that you describe here. Also, as I alluded to above, JavaScript is so terrible in so many ways that the mere idea of imitating it inherently makes me skeptical; there's almost nothing about JavaScript's design that couldn't be done better, and most of what it does are things that Python already does better and has done better for years. In short, I don't see any advantages at all to doing classes this way, and there are some non-negligible disadvantages. -- Brendan Barnwell "Do not follow where the path may lead. Go, instead, where there is no path, and leave a trail." --author unknown
On 05/14/2017 01:53 AM, Brendan Barnwell wrote:
On 2017-05-13 21:07, Simon Ramstedt wrote:
Here are the pros and cons I could come up with for the proposed method:
(+) Simpler and more explicit.
I don't really see how that's simpler or more explicit. In one respect it's clearly less explicit, as the "self" is implicit.
I cannot imagine that the average Python programmer would consider this to be simpler or more explicit than the current way of creating objects. Some purists may argue in your favor, saying that by not having the _x attribute your code is more robust as you cannot accidentally change _x later and end up modifying how a method works. I don't think this is worse. However, I do think it is unnecessary.
On Sun, May 14, 2017 at 2:53 PM, Brendan Barnwell <brenbarn@brenbarn.net> wrote:
Attributes aren't just for passing things to other methods. They're for storing state. In your proposed system, how would an object mutate one of its own attributes? It looks like "x" here is just stored in a function closure, which wouldn't allow easy mutation. Also, how would another object access the attribute from outside (as we currently do with self.x)? You can say we'd only use this new attribute-free approach when we want to pass a constructor argument that's used but never mutated or accessed from outside, but that severely restricts the potential use cases, and all it saves you is typing "self".
My expectation is that you'd be using "nonlocal x" to do that. Closures can be used to emulate classes (and vice versa). However, in Python, the syntax for reaching outside a method to access the closure is significantly clunkier than the equivalent in C-derived languages: // JavaScript function outer() { var x = 0; function inner() { x += 2; } } # Python def outer(): x = 0 def inner(): nonlocal x x += 2 Is it better to say "nonlocal" on everything you use than to say "self.x" on each use? I say no, because it makes copying and pasting code dangerous - reading attributes will work, but mutating them requires the nonlocal tag. With "self.x", it's the same on both, and it's the same in all methods. ChrisA
On 2017-05-14 00:34, Chris Angelico wrote:
On Sun, May 14, 2017 at 2:53 PM, Brendan Barnwell <brenbarn@brenbarn.net> wrote:
Attributes aren't just for passing things to other methods. They're for storing state. In your proposed system, how would an object mutate one of its own attributes? It looks like "x" here is just stored in a function closure, which wouldn't allow easy mutation. Also, how would another object access the attribute from outside (as we currently do with self.x)? You can say we'd only use this new attribute-free approach when we want to pass a constructor argument that's used but never mutated or accessed from outside, but that severely restricts the potential use cases, and all it saves you is typing "self".
My expectation is that you'd be using "nonlocal x" to do that.
That would allow mutation from within methods, but (as far as I can tell) not access (or mutation) from outside the class. -- Brendan Barnwell "Do not follow where the path may lead. Go, instead, where there is no path, and leave a trail." --author unknown
Chris Angelico wrote:
# Python def outer(): x = 0 def inner(): nonlocal x x += 2
Is it better to say "nonlocal" on everything you use than to say "self.x" on each use?
I've not used Python closures since nonlocal came about, but AFAIK you only need to use nonlocal if there's an assignment to the variable in the scope. Maybe an augmented assignment creates the same constraint but logically it shouldn't.
On Wed, May 17, 2017 at 11:08 PM, Boris Borcic <bborcic@gmail.com> wrote:
Chris Angelico wrote:
# Python def outer(): x = 0 def inner(): nonlocal x x += 2
Is it better to say "nonlocal" on everything you use than to say "self.x" on each use?
I've not used Python closures since nonlocal came about, but AFAIK you only need to use nonlocal if there's an assignment to the variable in the scope. Maybe an augmented assignment creates the same constraint but logically it shouldn't.
Augmented assignment is still assignment. The rules for the nonlocal keyword are the same as for the global keyword, so you can tinker with that if you want a comparison. Technically my statement wasn't quite true ("on everything" ignores the fact that *reading* a closed-over variable doesn't require any declaration), but omitting the nonlocal declaration would leave you open to incredibly sneaky bugs where moving code from one method to another changes its semantics. So you'd probably end up wanting some sort of class-level declaration that says "please make these nonlocal in all enclosed scopes" (which you could do with macropy, I'm sure), to avoid the risk of missing one. And at that point, you're really asking for a C++ style of thing where your variables get declared, and someone's going to ask you why you're writing this in Python :) ChrisA
Hi, thanks a lot for your feedback! On Sun, May 14, 2017, 00:54 Brendan Barnwell <brenbarn@brenbarn.net> wrote:
On 2017-05-13 21:07, Simon Ramstedt wrote:
Hi, do you have an opinion on the following?
My general opinion is that imitating JavaScript is almost always a bad idea. :-)
Wouldn't it be nice to define classes via a simple constructor function (as below) instead of a conventional class definition?
*conventional*: | classMyClass(ParentClass): def__init__(x): self._x=x defmy_method(y): z=self._x+y returnz |
*proposed*:
| defMyClass(x): self=ParentClass() defmy_method(y): z=x+y returnz self.my_method=my_method # that's cumbersome (see comments below) returnself |
Here are the pros and cons I could come up with for the proposed method:
(+) Simpler and more explicit.
I don't really see how that's simpler or more explicit. In one respect it's clearly less explicit, as the "self" is implicit.
(+) No need to create attributes (like `self._x`) just to pass something from `__init__` to another method.
Attributes aren't just for passing things to other methods. They're for storing state. In your proposed system, how would an object mutate one of its own attributes? It looks like "x" here is just stored in a function closure, which wouldn't allow easy mutation. Also, how would another object access the attribute from outside (as we currently do with self.x)? You can say we'd only use this new attribute-free approach when we want to pass a constructor argument that's used but never mutated or accessed from outside, but that severely restricts the potential use cases, and all it saves you is typing "self".
Attributes could be added to self just as in conventional classes if they are needed.
Relatedly, how is ParentClass itself defined? I don't see how you could bootstrap this without having a real class at the bottom of it somehow (as your test script in fact does).
You could bootstrap with an object base class/constructor just as normal classes inherit from object. Also the normal class system should remain in any case in order not to break every python library.
(+) Default arguments / annotations for methods could be different for each class instance. Adaptive defaults wouldn't have to simulated with a None.
That seems as likely to be a negative as a positive. Having different instances with different default values could be confusing. This would even allow different instances to define totally different methods (with if-logic inside the function constructor), which would definitely be confusing.
Different default values for different instances are a corner case but they are already happening by setting default to None. Defining different methods for different instances wouldn't be good but that is also possible with conventional classes (by adding functions to self in __init__).
(+) Class/instance level imports would work.
How often is that really needed?
True, usually it doesn't matter. But when using big packages like tensorflow that take several seconds to load it can be annoying. Its always loaded when importing any library that uses it internally, because of module level imports that should be class/instance level. Even if we just wanted to do --help on the command line and needed that library before argparse for some reason.
(-/+) Speed: The `def`-based objects take 0.6 μs to create while the
`class`-based objects take only 0.4 μs. For method execution however the closure takes only 0.15 μs while the proper method takes 0.22 μs (script <https://gist.github.com/rmst/78b2b0f56a3d9ec13b1ec6f3bd50aa9c>).
I don't think you can really evaluate the performance impact of this alternative just based on a trivial example like that.
Agree, I don't know really how well this would perform.
(-/+) Checking types: In the proposed example above the returned object
wouldn't know that it has been created by `MyClass`. There are a couple of solutions to that, though. The easiest to implement would be to change the first line to `self = subclass(ParentClass())` where the subclass function looks at the next item in the call stack (i.e. `MyClass`) and makes it the type of the object. Another solution would be to have a special rule for functions with capital first letter returning a single object to append itself to the list of types of the returned object. Alternatively there could be a special keyword e.g. `classdef` that would be used instead of `def` if we wouldn't want to rely on the name.
Those special rules sound very hackish to me.
(-) The current syntax for adding a function to an object is cumbersome. That's what is preventing me from actually using the proposed pattern. But is this really the only reason for not using it? And if so, wouldn't that be a good argument for enabling something like below? * * *attribute function definitions*: | defMyClass(x): self=ParentClass() defself.my_method(y): z=x+y returnz returnself |
or alternatively*multiline lambdas*:
| defMyClass(x): self=ParentClass() self.my_method=(y): z=x+y returnz returnself |
To be honest, from all your examples, I don't really see what the point is. It's a different syntax for doing some of the same things the existing class syntax does, while providing no apparent way to do some important things (like mutating attributes). I think Python's existing class syntax is simple, clear, and quite nice overall, and creating class instances by calling a function instead of a class doesn't add anything. In fact, even JavaScript has recently added classes to allow programmers to move away from the old approach that you describe here. Also, as I alluded to above, JavaScript is so terrible in so many ways that the mere idea of imitating it inherently makes me skeptical; there's almost nothing about JavaScript's design that couldn't be done better, and most of what it does are things that Python already does better and has done better for years. In short, I don't see any advantages at all to doing classes this way, and there are some non-negligible disadvantages.
Interesting, didn't know that about Javascript. I also don't like Javascript's prototypes very much but thought adding "JavaScript-like" to the title might help explain what I meant. Leaving the possible replacement for classes aside, do you have an opinion specifically about the following? def obj.my_function(a, b): ... as syntactic sugar for def my_function(a, b): ... obj.my_function = my_function In my experience this pattern comes actually up quite a bit. E.g. when working with these "symbolic" machine learning frameworks like theano or tensorflow. Apart from that it mixins very easy. What do you think are the odds of something like this actually making it into the Python and if greater than 0 in which timeframe?
-- Brendan Barnwell "Do not follow where the path may lead. Go, instead, where there is no path, and leave a trail." --author unknown _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
Thanks, Simon
FWIW, Javascript itself is moving away from this syntax in favour of a more Python-like syntax based on the 'class' keyword. This was introduced in EcmaScript 2015. Stephan Op 14 mei 2017 09:35 schreef "Simon Ramstedt" <simonramstedt@gmail.com>:
Hi, thanks a lot for your feedback!
On Sun, May 14, 2017, 00:54 Brendan Barnwell <brenbarn@brenbarn.net> wrote:
On 2017-05-13 21:07, Simon Ramstedt wrote:
Hi, do you have an opinion on the following?
My general opinion is that imitating JavaScript is almost always a bad idea. :-)
Wouldn't it be nice to define classes via a simple constructor function (as below) instead of a conventional class definition?
*conventional*: | classMyClass(ParentClass): def__init__(x): self._x=x defmy_method(y): z=self._x+y returnz |
*proposed*:
| defMyClass(x): self=ParentClass() defmy_method(y): z=x+y returnz self.my_method=my_method # that's cumbersome (see comments below) returnself |
Here are the pros and cons I could come up with for the proposed method:
(+) Simpler and more explicit.
I don't really see how that's simpler or more explicit. In one respect it's clearly less explicit, as the "self" is implicit.
(+) No need to create attributes (like `self._x`) just to pass something from `__init__` to another method.
Attributes aren't just for passing things to other methods. They're for storing state. In your proposed system, how would an object mutate one of its own attributes? It looks like "x" here is just stored in a function closure, which wouldn't allow easy mutation. Also, how would another object access the attribute from outside (as we currently do with self.x)? You can say we'd only use this new attribute-free approach when we want to pass a constructor argument that's used but never mutated or accessed from outside, but that severely restricts the potential use cases, and all it saves you is typing "self".
Attributes could be added to self just as in conventional classes if they are needed.
Relatedly, how is ParentClass itself defined? I don't see how you could bootstrap this without having a real class at the bottom of it somehow (as your test script in fact does).
You could bootstrap with an object base class/constructor just as normal classes inherit from object. Also the normal class system should remain in any case in order not to break every python library.
(+) Default arguments / annotations for methods could be different for each class instance. Adaptive defaults wouldn't have to simulated with a None.
That seems as likely to be a negative as a positive. Having different instances with different default values could be confusing. This would even allow different instances to define totally different methods (with if-logic inside the function constructor), which would definitely be confusing.
Different default values for different instances are a corner case but they are already happening by setting default to None. Defining different methods for different instances wouldn't be good but that is also possible with conventional classes (by adding functions to self in __init__).
(+) Class/instance level imports would work.
How often is that really needed?
True, usually it doesn't matter. But when using big packages like tensorflow that take several seconds to load it can be annoying. Its always loaded when importing any library that uses it internally, because of module level imports that should be class/instance level. Even if we just wanted to do --help on the command line and needed that library before argparse for some reason.
(-/+) Speed: The `def`-based objects take 0.6 μs to create while the
`class`-based objects take only 0.4 μs. For method execution however the closure takes only 0.15 μs while the proper method takes 0.22 μs (script <https://gist.github.com/rmst/78b2b0f56a3d9ec13b1ec6f3bd50aa9c>).
I don't think you can really evaluate the performance impact of this alternative just based on a trivial example like that.
Agree, I don't know really how well this would perform.
(-/+) Checking types: In the proposed example above the returned object
wouldn't know that it has been created by `MyClass`. There are a couple of solutions to that, though. The easiest to implement would be to change the first line to `self = subclass(ParentClass())` where the subclass function looks at the next item in the call stack (i.e. `MyClass`) and makes it the type of the object. Another solution would be to have a special rule for functions with capital first letter returning a single object to append itself to the list of types of the returned object. Alternatively there could be a special keyword e.g. `classdef` that would be used instead of `def` if we wouldn't want to rely on the name.
Those special rules sound very hackish to me.
(-) The current syntax for adding a function to an object is cumbersome. That's what is preventing me from actually using the proposed pattern. But is this really the only reason for not using it? And if so, wouldn't that be a good argument for enabling something like below? * * *attribute function definitions*: | defMyClass(x): self=ParentClass() defself.my_method(y): z=x+y returnz returnself |
or alternatively*multiline lambdas*:
| defMyClass(x): self=ParentClass() self.my_method=(y): z=x+y returnz returnself |
To be honest, from all your examples, I don't really see what the point is. It's a different syntax for doing some of the same things the existing class syntax does, while providing no apparent way to do some important things (like mutating attributes). I think Python's existing class syntax is simple, clear, and quite nice overall, and creating class instances by calling a function instead of a class doesn't add anything. In fact, even JavaScript has recently added classes to allow programmers to move away from the old approach that you describe here. Also, as I alluded to above, JavaScript is so terrible in so many ways that the mere idea of imitating it inherently makes me skeptical; there's almost nothing about JavaScript's design that couldn't be done better, and most of what it does are things that Python already does better and has done better for years. In short, I don't see any advantages at all to doing classes this way, and there are some non-negligible disadvantages.
Interesting, didn't know that about Javascript. I also don't like Javascript's prototypes very much but thought adding "JavaScript-like" to the title might help explain what I meant.
Leaving the possible replacement for classes aside, do you have an opinion specifically about the following?
def obj.my_function(a, b): ...
as syntactic sugar for
def my_function(a, b): ...
obj.my_function = my_function
In my experience this pattern comes actually up quite a bit. E.g. when working with these "symbolic" machine learning frameworks like theano or tensorflow. Apart from that it mixins very easy.
What do you think are the odds of something like this actually making it into the Python and if greater than 0 in which timeframe?
-- Brendan Barnwell "Do not follow where the path may lead. Go, instead, where there is no path, and leave a trail." --author unknown _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
Thanks,
Simon
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
How do you call methods from superclass, like super in classic style ? 2017-05-14 9:45 GMT+02:00 Stephan Houben <stephanh42@gmail.com>:
FWIW, Javascript itself is moving away from this syntax in favour of a more Python-like syntax based on the 'class' keyword. This was introduced in EcmaScript 2015.
Stephan
Op 14 mei 2017 09:35 schreef "Simon Ramstedt" <simonramstedt@gmail.com>:
Hi, thanks a lot for your feedback!
On Sun, May 14, 2017, 00:54 Brendan Barnwell <brenbarn@brenbarn.net> wrote:
On 2017-05-13 21:07, Simon Ramstedt wrote:
Hi, do you have an opinion on the following?
My general opinion is that imitating JavaScript is almost always a bad idea. :-)
Wouldn't it be nice to define classes via a simple constructor function (as below) instead of a conventional class definition?
*conventional*: | classMyClass(ParentClass): def__init__(x): self._x=x defmy_method(y): z=self._x+y returnz |
*proposed*:
| defMyClass(x): self=ParentClass() defmy_method(y): z=x+y returnz self.my_method=my_method # that's cumbersome (see comments below) returnself |
Here are the pros and cons I could come up with for the proposed method:
(+) Simpler and more explicit.
I don't really see how that's simpler or more explicit. In one respect it's clearly less explicit, as the "self" is implicit.
(+) No need to create attributes (like `self._x`) just to pass something from `__init__` to another method.
Attributes aren't just for passing things to other methods. They're for storing state. In your proposed system, how would an object mutate one of its own attributes? It looks like "x" here is just stored in a function closure, which wouldn't allow easy mutation. Also, how would another object access the attribute from outside (as we currently do with self.x)? You can say we'd only use this new attribute-free approach when we want to pass a constructor argument that's used but never mutated or accessed from outside, but that severely restricts the potential use cases, and all it saves you is typing "self".
Attributes could be added to self just as in conventional classes if they are needed.
Relatedly, how is ParentClass itself defined? I don't see how you could bootstrap this without having a real class at the bottom of it somehow (as your test script in fact does).
You could bootstrap with an object base class/constructor just as normal classes inherit from object. Also the normal class system should remain in any case in order not to break every python library.
(+) Default arguments / annotations for methods could be different for each class instance. Adaptive defaults wouldn't have to simulated with a None.
That seems as likely to be a negative as a positive. Having different instances with different default values could be confusing. This would even allow different instances to define totally different methods (with if-logic inside the function constructor), which would definitely be confusing.
Different default values for different instances are a corner case but they are already happening by setting default to None. Defining different methods for different instances wouldn't be good but that is also possible with conventional classes (by adding functions to self in __init__).
(+) Class/instance level imports would work.
How often is that really needed?
True, usually it doesn't matter. But when using big packages like tensorflow that take several seconds to load it can be annoying. Its always loaded when importing any library that uses it internally, because of module level imports that should be class/instance level. Even if we just wanted to do --help on the command line and needed that library before argparse for some reason.
`class`-based objects take only 0.4 μs. For method execution however
(-/+) Speed: The `def`-based objects take 0.6 μs to create while the the
closure takes only 0.15 μs while the proper method takes 0.22 μs (script <https://gist.github.com/rmst/78b2b0f56a3d9ec13b1ec6f3bd50aa9c>).
I don't think you can really evaluate the performance impact of this alternative just based on a trivial example like that.
Agree, I don't know really how well this would perform.
(-/+) Checking types: In the proposed example above the returned object
wouldn't know that it has been created by `MyClass`. There are a couple of solutions to that, though. The easiest to implement would be to change the first line to `self = subclass(ParentClass())` where the subclass function looks at the next item in the call stack (i.e. `MyClass`) and makes it the type of the object. Another solution would be to have a special rule for functions with capital first letter returning a single object to append itself to the list of types of the returned object. Alternatively there could be a special keyword e.g. `classdef` that would be used instead of `def` if we wouldn't want to rely on the name.
Those special rules sound very hackish to me.
(-) The current syntax for adding a function to an object is cumbersome. That's what is preventing me from actually using the proposed pattern. But is this really the only reason for not using it? And if so, wouldn't that be a good argument for enabling something like below? * * *attribute function definitions*: | defMyClass(x): self=ParentClass() defself.my_method(y): z=x+y returnz returnself |
or alternatively*multiline lambdas*:
| defMyClass(x): self=ParentClass() self.my_method=(y): z=x+y returnz returnself |
To be honest, from all your examples, I don't really see what the point is. It's a different syntax for doing some of the same things the existing class syntax does, while providing no apparent way to do some important things (like mutating attributes). I think Python's existing class syntax is simple, clear, and quite nice overall, and creating class instances by calling a function instead of a class doesn't add anything. In fact, even JavaScript has recently added classes to allow programmers to move away from the old approach that you describe here. Also, as I alluded to above, JavaScript is so terrible in so many ways that the mere idea of imitating it inherently makes me skeptical; there's almost nothing about JavaScript's design that couldn't be done better, and most of what it does are things that Python already does better and has done better for years. In short, I don't see any advantages at all to doing classes this way, and there are some non-negligible disadvantages.
Interesting, didn't know that about Javascript. I also don't like Javascript's prototypes very much but thought adding "JavaScript-like" to the title might help explain what I meant.
Leaving the possible replacement for classes aside, do you have an opinion specifically about the following?
def obj.my_function(a, b): ...
as syntactic sugar for
def my_function(a, b): ...
obj.my_function = my_function
In my experience this pattern comes actually up quite a bit. E.g. when working with these "symbolic" machine learning frameworks like theano or tensorflow. Apart from that it mixins very easy.
What do you think are the odds of something like this actually making it into the Python and if greater than 0 in which timeframe?
-- Brendan Barnwell "Do not follow where the path may lead. Go, instead, where there is no path, and leave a trail." --author unknown _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
Thanks,
Simon
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
-- Antoine Rozo
On Sun, May 14, 2017 at 07:35:38AM +0000, Simon Ramstedt wrote:
Leaving the possible replacement for classes aside, do you have an opinion specifically about the following?
def obj.my_function(a, b): ...
as syntactic sugar for
def my_function(a, b): ...
obj.my_function = my_function
Personally, I don't object to it, I can see the idea has some merit. See the most recent discussion here: https://mail.python.org/pipermail/python-ideas/2017-February/044551.html
In my experience this pattern comes actually up quite a bit. E.g. when working with these "symbolic" machine learning frameworks like theano or tensorflow. Apart from that it mixins very easy.
What do you think are the odds of something like this actually making it into the Python and if greater than 0 in which timeframe?
Somebody would need to propose some compelling use-cases for where this is clearly better than the status quo. Guido would have to not object. Somebody would have to volunteer to do the work, and it would have to not cause an unacceptible performance hit. (I doubt that it would, since it's just a small amount of new syntactic sugar.) If you can show actual real-life code that would be improved by this new feature, that would increase the probability. If you can prove that it would be a benefit to (let's say) the Theano community, that would increase the probability. If you volunteered to do the work, rather than expect somebody else to do it, that would *significantly* increase the probability of it actually happening, provided the suggestion was actually accepted. If you read the previous discussion, I think the conclusion was that there is nothing that def Some_Object.name(x): ... can do that cannot be emulated by a decorator: @inject(Some_Object) def name(x): ... *except* that the decorator solution would leave the function "name" polluting the current namespace, and the new syntax could avoid that. And even that is easy to work-around: del name So I think the probability is low, but not zero. It would help if you could prove a significant real-world use-case for injecting functions into existing objects. -- Steve
On Sun, May 14, 2017 at 12:35 AM, Simon Ramstedt <simonramstedt@gmail.com> wrote:
What do you think are the odds of something like this actually making it into the Python and if greater than 0 in which timeframe?
If you're asking for language or stdlib support or an official endorsement, the odds are exactly zero. Of course if this pattern is useful for you in the context of something you are developing you are free to use it, presuming it doesn't require language or stdlib changes. -- --Guido van Rossum (python.org/~guido)
On Sun, May 14, 2017 at 04:07:44AM +0000, Simon Ramstedt wrote:
Hi, do you have an opinion on the following?
Hi, and welcome, and of course we have an opinion! This is Python-Ideas, we're very opinionated :-)
Wouldn't it be nice to define classes via a simple constructor function (as below) instead of a conventional class definition?
No.
*conventional*:
class MyClass(ParentClass): def __init__(x): self._x = x def my_method(y): z = self._x + y return z
Looks good to me. It is nicely explicit that you're creating a class, the superclass or superclasses are easy to see, and the attributes are explicit.
*proposed*:
def MyClass(x):
That is the exact same syntax for defining a function called "MyClass", that takes one argument, x. How is Python (and the reader!) supposed to tell which calls to def return a class and which return a function?
self = ParentClass()
What if you have multiple parent classes? Why is self an instance of the parent class, instead of MyClass?
def my_method(y): z = x + y return z
The local variable x is not defined. Wait, is that supposed to come from the closure def MyClass(x)? What if your class has twenty methods, each of which takes different arguments? Do you have to write: def MyClass(x, # used in my_method y, # used in another_method z, # used in third_method, a, b, c, # used in fourth_method ... # blah blah blah ): How does this generalise to non-toy classes, classes with more than one method?
self.my_method = my_method # that's cumbersome (see comments below) return self
Here are the pros and cons I could come up with for the proposed method:
(+) Simpler and more explicit.
I think you mean "More complicated and less explicit". Is this supposed to be some sort of prototype-based OOP instead of class-based OOP? I'd be interested in investigating prototype-based objects, but I don't think this is the way to do it. -- Steve
Whatever you all propose, coming from a java and c++ background, OOP in python is quite cumbersome. if you tell that i am not a python guy, then consider that current oop style does not reflect python's style of ease and simplicity is __init__ really a good syntax choice? Abdur-Rahmaan Janhangeer, Mauritius https://abdurrahmaanjanhangeer.wordpress.com On 14 May 2017 11:05, "Steven D'Aprano" <steve@pearwood.info> wrote:
On Sun, May 14, 2017 at 04:07:44AM +0000, Simon Ramstedt wrote:
Hi, do you have an opinion on the following?
Hi, and welcome, and of course we have an opinion! This is Python-Ideas, we're very opinionated :-)
Wouldn't it be nice to define classes via a simple constructor function (as below) instead of a conventional class definition?
No.
*conventional*:
class MyClass(ParentClass): def __init__(x): self._x = x def my_method(y): z = self._x + y return z
Looks good to me. It is nicely explicit that you're creating a class, the superclass or superclasses are easy to see, and the attributes are explicit.
*proposed*:
def MyClass(x):
That is the exact same syntax for defining a function called "MyClass", that takes one argument, x. How is Python (and the reader!) supposed to tell which calls to def return a class and which return a function?
self = ParentClass()
What if you have multiple parent classes?
Why is self an instance of the parent class, instead of MyClass?
def my_method(y): z = x + y return z
The local variable x is not defined. Wait, is that supposed to come from the closure def MyClass(x)?
What if your class has twenty methods, each of which takes different arguments? Do you have to write:
def MyClass(x, # used in my_method y, # used in another_method z, # used in third_method, a, b, c, # used in fourth_method ... # blah blah blah ):
How does this generalise to non-toy classes, classes with more than one method?
self.my_method = my_method # that's cumbersome (see comments
below)
return self
Here are the pros and cons I could come up with for the proposed method:
(+) Simpler and more explicit.
I think you mean "More complicated and less explicit".
Is this supposed to be some sort of prototype-based OOP instead of class-based OOP? I'd be interested in investigating prototype-based objects, but I don't think this is the way to do it.
-- Steve _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
On Sun, May 14, 2017 at 11:12:21AM +0400, Abdur-Rahmaan Janhangeer wrote:
Whatever you all propose,
coming from a java and c++ background, OOP in python is quite cumbersome.
In what way it is cumbersome?
if you tell that i am not a python guy, then consider that current oop style does not reflect python's style of ease and simplicity
That is one opinion.
is __init__ really a good syntax choice?
I don't understand the question. Are you complaining about the name "__init__"? Do you think it would be easier to write if it was spelled "init" or "new" or "Constructor"? -- Steve
Also, how do you handle special methods for operators, such as __add__? 2017-05-14 13:18 GMT+02:00 Steven D'Aprano <steve@pearwood.info>:
On Sun, May 14, 2017 at 11:12:21AM +0400, Abdur-Rahmaan Janhangeer wrote:
Whatever you all propose,
coming from a java and c++ background, OOP in python is quite cumbersome.
In what way it is cumbersome?
if you tell that i am not a python guy, then consider that current oop style does not reflect python's style of ease and simplicity
That is one opinion.
is __init__ really a good syntax choice?
I don't understand the question. Are you complaining about the name "__init__"? Do you think it would be easier to write if it was spelled "init" or "new" or "Constructor"?
-- Steve _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
-- Antoine Rozo
On Sun, May 14, 2017 at 01:33:32PM +0200, Antoine Rozo wrote:
Also, how do you handle special methods for operators, such as __add__?
Oh, that's a good point! I forgot about that. For implementation-dependent reasons, you couldn't use this proposed new syntax for dunder methods: def MyClass(): self = subclass(Parent) def my_method(arg): ... self.my_method = my_method def __str__(): ... self.__str__ = __str__ return self obj = MyClass() obj.my_method(123) # okay obj.__str__() # works, but bad style str(obj) # doesn't work in CPython Because of the implementation, str(obj) would NOT call __str__ in CPython, although I think it would in IronPython. I'm not sure about PyPy or Jython. (CPython "new style classes" only call __dunder__ methods when they are defined on the class, or a superclass, not when they are in the instance __dict__.) -- Steve
On 14 May 2017 at 17:12, Abdur-Rahmaan Janhangeer <arj.python@gmail.com> wrote:
Whatever you all propose,
coming from a java and c++ background, OOP in python is quite cumbersome.
if you tell that i am not a python guy, then consider that current oop style does not reflect python's style of ease and simplicity
is __init__ really a good syntax choice?
That's a different question, and one with a well-structured third party solution: https://attrs.readthedocs.io/en/stable/ See https://mail.python.org/pipermail/python-ideas/2017-April/045514.html for some ideas on how something like attrs might be adapted to provide better standard library tooling for more concise and readable class definitions. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia
This should be worked into a PEP, instead of living on as a bunch of python-ideas posts and blogs. I find the attrs documentation (and Glyph's blog post about it) almost unreadable because of the exalted language -- half the doc seems to be *selling* the library more than *explaining* it. If this style were to become common I would find it a disturbing trend. But having something alongside NamedTuple that helps you declare classes with mutable attributes using the new PEP 526 syntax (and maybe a few variants) would definitely be useful. Will someone please write a PEP? Very few of the specifics of attrs need be retained (its punny naming choices are too much for the stdlib). --Guido On Mon, May 15, 2017 at 4:05 AM, Nick Coghlan <ncoghlan@gmail.com> wrote:
Whatever you all propose,
coming from a java and c++ background, OOP in python is quite cumbersome.
if you tell that i am not a python guy, then consider that current oop
On 14 May 2017 at 17:12, Abdur-Rahmaan Janhangeer <arj.python@gmail.com> wrote: style
does not reflect python's style of ease and simplicity
is __init__ really a good syntax choice?
That's a different question, and one with a well-structured third party solution: https://attrs.readthedocs.io/en/stable/
See https://mail.python.org/pipermail/python-ideas/2017-April/045514.html for some ideas on how something like attrs might be adapted to provide better standard library tooling for more concise and readable class definitions.
Cheers, Nick.
-- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
-- --Guido van Rossum (python.org/~guido)
This should be worked into a PEP, instead of living on as a bunch of
On Mon, May 15, 2017 at 6:30 PM Guido van Rossum <guido@python.org> wrote: python-ideas posts and blogs. ...
Will someone please write a PEP?
If by "this" you mean adding to stdlib something like @record class Point: x: int y: int or something along the lines of my "modifiers" proposal ( https://mail.python.org/pipermail//python-ideas/2016-September/042360.html), then I think I would like to help writing such a PEP. But I thought these proposals were rejected. Elazar
I expect that we will need someone with a really good sensibility for Pythonic language/API design to lead the PEP writing. On Mon, May 15, 2017 at 9:03 AM, אלעזר <elazarg@gmail.com> wrote:
This should be worked into a PEP, instead of living on as a bunch of
On Mon, May 15, 2017 at 6:30 PM Guido van Rossum <guido@python.org> wrote: python-ideas posts and blogs. ...
Will someone please write a PEP?
If by "this" you mean adding to stdlib something like
@record class Point: x: int y: int
or something along the lines of my "modifiers" proposal ( https://mail.python.org/pipermail//python-ideas/2016-September/042360.html), then I think I would like to help writing such a PEP. But I thought these proposals were rejected.
Elazar
-- --Guido van Rossum (python.org/~guido)
On 15 May 2017 at 18:22, Guido van Rossum <guido@python.org> wrote:
I expect that we will need someone with a really good sensibility for Pythonic language/API design to lead the PEP writing.
I probably don't have good sensibility for Pythonic API design yet (and I am more focused on PEP 544) so I cannot lead this, but I would like to actively participate in writing. -- Ivan
I could also try this myself in my spare time at PyCon (surprisingly, I have some!). It sounds kind of interesting. However I've never used the 'attrs' package... On Tue, May 16, 2017 at 7:52 AM, Ivan Levkivskyi <levkivskyi@gmail.com> wrote:
On 15 May 2017 at 18:22, Guido van Rossum <guido@python.org> wrote:
I expect that we will need someone with a really good sensibility for Pythonic language/API design to lead the PEP writing.
I probably don't have good sensibility for Pythonic API design yet (and I am more focused on PEP 544) so I cannot lead this, but I would like to actively participate in writing.
-- Ivan
-- --Guido van Rossum (python.org/~guido)
I wouldn't mind discussing it at PyCon. I'm just starting a project to switch to attrs, and I'm reasonably familiar with it. -- Eric.
On May 16, 2017, at 10:53 AM, Guido van Rossum <guido@python.org> wrote:
I could also try this myself in my spare time at PyCon (surprisingly, I have some!). It sounds kind of interesting. However I've never used the 'attrs' package...
On Tue, May 16, 2017 at 7:52 AM, Ivan Levkivskyi <levkivskyi@gmail.com> wrote:
On 15 May 2017 at 18:22, Guido van Rossum <guido@python.org> wrote: I expect that we will need someone with a really good sensibility for Pythonic language/API design to lead the PEP writing.
I probably don't have good sensibility for Pythonic API design yet (and I am more focused on PEP 544) so I cannot lead this, but I would like to actively participate in writing.
-- Ivan
-- --Guido van Rossum (python.org/~guido) _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
On Wed, May 17, 2017 at 12:53 AM, Guido van Rossum <guido@python.org> wrote:
I could also try this myself in my spare time at PyCon (surprisingly, I have some!). It sounds kind of interesting. However I've never used the 'attrs' package...
Me neither, so I'm not really an ideal person to head this up. Is there anyone who (a) knows what is and isn't Pythonic, (b) has used 'attrs', and (c) has spare time? It's not an easy trifecta but we can hope! ChrisA
Maybe Lukasz is interested? On May 16, 2017 8:00 AM, "Chris Angelico" <rosuav@gmail.com> wrote:
On Wed, May 17, 2017 at 12:53 AM, Guido van Rossum <guido@python.org> wrote:
I could also try this myself in my spare time at PyCon (surprisingly, I have some!). It sounds kind of interesting. However I've never used the 'attrs' package...
Me neither, so I'm not really an ideal person to head this up. Is there anyone who (a) knows what is and isn't Pythonic, (b) has used 'attrs', and (c) has spare time? It's not an easy trifecta but we can hope!
ChrisA _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
Maybe we can bring this up as a lightning talk at the language summit to see who in the room has the appropriate background knowledge? And obviously someone can talk to Hynek to see if he wants to provide input based on community feedback for attrs and lessons learned. On Tue, 16 May 2017 at 08:11 Guido van Rossum <gvanrossum@gmail.com> wrote:
Maybe Lukasz is interested?
On May 16, 2017 8:00 AM, "Chris Angelico" <rosuav@gmail.com> wrote:
On Wed, May 17, 2017 at 12:53 AM, Guido van Rossum <guido@python.org> wrote:
I could also try this myself in my spare time at PyCon (surprisingly, I have some!). It sounds kind of interesting. However I've never used the 'attrs' package...
Me neither, so I'm not really an ideal person to head this up. Is there anyone who (a) knows what is and isn't Pythonic, (b) has used 'attrs', and (c) has spare time? It's not an easy trifecta but we can hope!
ChrisA _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
Hi all, Thanks to this thread I learned about the "attrs" library. I am a heavy namedtuple (ab)user but I think I will be using attrs going forward. If something like attrs would made it in the standard library it would be awesome. Thanks, Stephan 2017-05-16 20:08 GMT+02:00 Brett Cannon <brett@python.org>:
Maybe we can bring this up as a lightning talk at the language summit to see who in the room has the appropriate background knowledge? And obviously someone can talk to Hynek to see if he wants to provide input based on community feedback for attrs and lessons learned.
On Tue, 16 May 2017 at 08:11 Guido van Rossum <gvanrossum@gmail.com> wrote:
Maybe Lukasz is interested?
On May 16, 2017 8:00 AM, "Chris Angelico" <rosuav@gmail.com> wrote:
On Wed, May 17, 2017 at 12:53 AM, Guido van Rossum <guido@python.org> wrote:
I could also try this myself in my spare time at PyCon (surprisingly, I have some!). It sounds kind of interesting. However I've never used the 'attrs' package...
Me neither, so I'm not really an ideal person to head this up. Is there anyone who (a) knows what is and isn't Pythonic, (b) has used 'attrs', and (c) has spare time? It's not an easy trifecta but we can hope!
ChrisA _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
Stephen, What features of attrs specifically solve your use cases? --Guido On Tue, May 16, 2017 at 12:18 PM, Stephan Houben <stephanh42@gmail.com> wrote:
Hi all,
Thanks to this thread I learned about the "attrs" library. I am a heavy namedtuple (ab)user but I think I will be using attrs going forward.
If something like attrs would made it in the standard library it would be awesome.
Thanks,
Stephan
2017-05-16 20:08 GMT+02:00 Brett Cannon <brett@python.org>:
Maybe we can bring this up as a lightning talk at the language summit to see who in the room has the appropriate background knowledge? And obviously someone can talk to Hynek to see if he wants to provide input based on community feedback for attrs and lessons learned.
On Tue, 16 May 2017 at 08:11 Guido van Rossum <gvanrossum@gmail.com> wrote:
Maybe Lukasz is interested?
On May 16, 2017 8:00 AM, "Chris Angelico" <rosuav@gmail.com> wrote:
On Wed, May 17, 2017 at 12:53 AM, Guido van Rossum <guido@python.org> wrote:
I could also try this myself in my spare time at PyCon
(surprisingly, I
have some!). It sounds kind of interesting. However I've never used the 'attrs' package...
Me neither, so I'm not really an ideal person to head this up. Is there anyone who (a) knows what is and isn't Pythonic, (b) has used 'attrs', and (c) has spare time? It's not an easy trifecta but we can hope!
ChrisA _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
-- --Guido van Rossum (python.org/~guido)
On Tue, May 16, 2017 at 5:04 PM, Guido van Rossum <gvanrossum@gmail.com> wrote: What features of attrs specifically solve your use cases?
(not Stephen) I hadn’t thought about this use case: In [1]: class C(): ...: x = 1 ...: ...: def __init__(self, x=None): ...: if x is not None: ...: self.x = x ...: ...: def __str__(self): ...: return 'C(%s)' % self.x ...: In [2]: c1 = C() ...: c2 = C(2) ...: In [3]: print(c1, c2) C(1) C(2) And I might use it here on. What I like about attrs is: - The class level declaration of instance attributes - That the reasonable *init*, *repr*, and *eq* are generated I don’t like the excessive wordiness in attrs, and I don’t need “the kitchen sink” be available to have instance attributes declared at the class level. A solution based on the typing module would be much better. Basically, Python is lacking a way to declare instance fields with default values away of the initializer. Several of the mainstream OO languages (Java, Swift, Go) provide for that. I haven’t thought much about this, except about if there’s indeed a need (and there is), but I can’t know if the solution if through decorators, or inheritance. -- Juancarlo *Añez*
On Tue, May 16, 2017 at 8:14 PM, Juancarlo Añez <apalala@gmail.com> wrote:
What I like about attrs is:
- The class level declaration of instance attributes - That the reasonable *init*, *repr*, and *eq* are generated
OK, the former should be doable using PEP 526 (the type is stored in __annotations__ and the default in the class dict given to the metaclass), while the latter should be doable using a standard metaclass -- assuming we can agree on what the "reasonable" __init__, __repr__ and __eq__ should do.
I don’t like the excessive wordiness in attrs,
Really? @attr.s is wordy? :-) I think it's deadly cute. (The only library I've ever seen that did something worse was "monocle" which used @_o.)
and I don’t need “the kitchen sink” be available to have instance attributes declared at the class level. A solution based on the typing module would be much better.
That's what I am hoping, yes.
Basically, Python is lacking a way to declare instance fields with default values away of the initializer. Several of the mainstream OO languages (Java, Swift, Go) provide for that.
Hm, there are some issues here of course -- while it's simple to set the default to e.g. 0, (1, 2, 3) or '<string>', it's not so easy to set a default to [] or {'foo': 'bar'} unless you just state "do whatever copy.copy() does".
I haven’t thought much about this, except about if there’s indeed a need (and there is), but I can’t know if the solution if through decorators, or inheritance.
I suppose we could do it using either a class decorator or a metaclass -- we'll have to compare the pros and cons and specific use cases to choose. (Example: https://github.com/python/typing/issues/427.) -- --Guido van Rossum (python.org/~guido <http://python.org/%7Eguido>)
Le 17/05/2017 à 07:22, Guido van Rossum a écrit :
On Tue, May 16, 2017 at 8:14 PM, Juancarlo Añez <apalala@gmail.com <mailto:apalala@gmail.com>> wrote:
What I like about attrs is:
* The class level declaration of instance attributes * That the reasonable *init*, *repr*, and *eq* are generated
OK, the former should be doable using PEP 526 (the type is stored in __annotations__ and the default in the class dict given to the metaclass), while the latter should be doable using a standard metaclass -- assuming we can agree on what the "reasonable" __init__, __repr__ and __eq__ should do.
I don’t like the excessive wordiness in attrs,
Really? @attr.s is wordy? :-) I think it's deadly cute. (The only library I've ever seen that did something worse was "monocle" which used @_o.)
import attr @attr.s ... class Point: ... x = attr.ib(default=42) ... y = attr.ib(default=attr.Factory(list))
Is pretty wordy compared to something like another language would do such as: class Point: int x = 42 list y = [] Now I get that: - Python already has a similar syntax creating class attributes. - You can't put mutable objects here. - attr does more since it generates dunder methods. But having an import, a decorator and verbose calls to attr.ib does not feel like idiomatic Python at all. Python is an elegant and expressive language. This is none of the above. Also Python is beginner friendly. Now OPP is already hard to teach to my students, but if I have to add this to the mix, I will just have to tell them to copy / paste it blindly for a long time before I can get to the point they can understand what it does. We should be able to find a middle ground. First, if we have something LIKE attr, should it need an import? Basic data structures may not require an import to work. async/await are way better than import @asyncio.coroutine. Secondly, while I really, really, really want this feature, I think we should not rush it. Some ideas have to be explored. E.G: Adding keywords for it ? I know adding a keyword is the worst thing one can suggest on this list ever. But it has to be mentioned because most other languages do it this way. class Point: instancevar x = 42 instancevar y = lazy [] # we add a debate about this a few months ago Adding a special syntax for it ? ruby has something similar. class Point: @x = 42 @@y = list Upgrading the class constructor? It does not address the mutablility issue though. class Point(x=42, y=[]) Mixing concepts? class Point(metaclass=autoclass(x=42, y=lazy [])): pass @args(x=42) @factory_args(y=list) @autodunder() class Point: pass @autoclass( x=42 y=autoclass.lazy(list) ) class Point: pass Just adding attrs, which is a workaround to a missing feature in Python, as a boiler plate and calling it a day seems unwise. Don't get me wrong, I like attrs, I like asyncio and I like the whole battery included concept. But we lived without it until now, so let's not go too fast on this.
Hi Michel,
Now OPP is already hard to teach to my students, but if I have to add this to the mix, I will just have to tell them to copy / paste it blindly for a long time before I can get to the point they can understand what it does.
About the teachability, some remarks: 1. You can still just skip it. I presume you don't teach all the advanced metaclass stuff right away either, even though it is part of core Python. 2. Is it really that complicated? attr.s is just a normal Python function, which adds some members to a class. You don't even have to use the decorator @ syntax, that is just a convenience. To me this seems easier to teach than yet another dedicated syntax. Stephan 2017-05-17 10:15 GMT+02:00 Michel Desmoulin <desmoulinmichel@gmail.com>:
Le 17/05/2017 à 07:22, Guido van Rossum a écrit :
On Tue, May 16, 2017 at 8:14 PM, Juancarlo Añez <apalala@gmail.com <mailto:apalala@gmail.com>> wrote:
What I like about attrs is:
* The class level declaration of instance attributes * That the reasonable *init*, *repr*, and *eq* are generated
OK, the former should be doable using PEP 526 (the type is stored in __annotations__ and the default in the class dict given to the metaclass), while the latter should be doable using a standard metaclass -- assuming we can agree on what the "reasonable" __init__, __repr__ and __eq__ should do.
I don’t like the excessive wordiness in attrs,
Really? @attr.s is wordy? :-) I think it's deadly cute. (The only library I've ever seen that did something worse was "monocle" which used @_o.)
import attr @attr.s ... class Point: ... x = attr.ib(default=42) ... y = attr.ib(default=attr.Factory(list))
Is pretty wordy compared to something like another language would do such as:
class Point: int x = 42 list y = []
Now I get that:
- Python already has a similar syntax creating class attributes. - You can't put mutable objects here. - attr does more since it generates dunder methods.
But having an import, a decorator and verbose calls to attr.ib does not feel like idiomatic Python at all. Python is an elegant and expressive language. This is none of the above. Also Python is beginner friendly. Now OPP is already hard to teach to my students, but if I have to add this to the mix, I will just have to tell them to copy / paste it blindly for a long time before I can get to the point they can understand what it does.
We should be able to find a middle ground.
First, if we have something LIKE attr, should it need an import? Basic data structures may not require an import to work. async/await are way better than import @asyncio.coroutine.
Secondly, while I really, really, really want this feature, I think we should not rush it.
Some ideas have to be explored.
E.G:
Adding keywords for it ? I know adding a keyword is the worst thing one can suggest on this list ever. But it has to be mentioned because most other languages do it this way.
class Point: instancevar x = 42 instancevar y = lazy [] # we add a debate about this a few months ago
Adding a special syntax for it ? ruby has something similar.
class Point: @x = 42 @@y = list
Upgrading the class constructor? It does not address the mutablility issue though.
class Point(x=42, y=[])
Mixing concepts?
class Point(metaclass=autoclass(x=42, y=lazy [])): pass
@args(x=42) @factory_args(y=list) @autodunder() class Point: pass
@autoclass( x=42 y=autoclass.lazy(list) ) class Point: pass
Just adding attrs, which is a workaround to a missing feature in Python, as a boiler plate and calling it a day seems unwise.
Don't get me wrong, I like attrs, I like asyncio and I like the whole battery included concept. But we lived without it until now, so let's not go too fast on this.
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
Le 17/05/2017 à 13:08, Stephan Houben a écrit :
Hi Michel,
Now OPP is already hard to teach to my students, but if I have to add this to the mix, I will just have to tell them to copy / paste it blindly for a long time before I can get to the point they can understand what it does.
About the teachability, some remarks: 1. You can still just skip it. I presume you don't teach all the advanced metaclass stuff right away either, even though it is part of core Python.
Comparing setting instance attributes to metaclasses is pushing it don't you think ? The thing is, teaching OOP is challenging enough. You have __init__ and all it's quicks, and the @decorators for class methods, and self, and cls, and inheritances. And that's just the basics, not talking about multiple inheritance, properties, other dunder methods or compostion. Having a cleaner, faster solution to declare a class would be awesome, both for dev and for teaching. That's why we all love attrs. But we are talking here about a nice-to-have feature. Python works perfectly fine without it. But since we are at it, let's make something great.
2. Is it really that complicated? attr.s is just a normal Python function, which adds some members to a class. You don't even have to use the decorator @ syntax, that is just a convenience. To me this seems easier to teach than yet another dedicated syntax.
My guess is that, without the decorator, the attributes don't do anything. They are just declarative hints for the decorator to do the magic. But even then, it's already an additional burden to have to explain the difference between this magic and the regular class attribute. So if I have to do it, I wish I could do it without having to explain also 2 namespaced function calls, one taking itself the return value of a call as a parameter, which BTW is a factory taking a constructor as a callback. Do not over estimate the skills of newcommers in programming. They have a lot of things to learn.
Hi Michel,
Comparing setting instance attributes to metaclasses is pushing it don't you think ?
My only point is that there are aspects of the core Python language which are probably not covered by your course (and I chose metaclasses as a topic least likely to be covered in a beginner's course). So this would justify skipping an 'attr' functionality if it would be deemed too complex for beginners.
My guess is that, without the decorator, the attributes don't do anything. They are just declarative hints for the decorator to do the magic.
I mean you don't need to use the @ syntax. You can just as well do: class MyClass: foo = attr.ib() MyClass = attr.s(MyClass) thereby stressing that `attr.s' is just a plain Python function, and there is no magic involved.
Do not over estimate the skills of newcomers in programming. They have a lot of things to learn.
I realize that. My point is only about the *relative* ease of learning of the current attr.s decorator-based approach vs. dedicated syntax. Perhaps both are too difficult for a beginner's course, but I would contend that the decorator approach is (relatively) simpler (perhaps in the same way that special relativity is simple compared to general relativity ;-) ). My argument for that is that the attr.s approach does not introduce any new mechanism, but is just an (advanced) application of basic mechanism such as function calls and attribute access. Hope this clarifies my position. Stephan 2017-05-17 13:37 GMT+02:00 Michel Desmoulin <desmoulinmichel@gmail.com>:
Le 17/05/2017 à 13:08, Stephan Houben a écrit :
Hi Michel,
Now OPP is already hard to teach to my students, but if I have to add this to the mix, I will just have to tell them to copy / paste it blindly for a long time before I can get to the point they can understand what it does.
About the teachability, some remarks: 1. You can still just skip it. I presume you don't teach all the advanced metaclass stuff right away either, even though it is part of core Python.
Comparing setting instance attributes to metaclasses is pushing it don't you think ?
The thing is, teaching OOP is challenging enough. You have __init__ and all it's quicks, and the @decorators for class methods, and self, and cls, and inheritances. And that's just the basics, not talking about multiple inheritance, properties, other dunder methods or compostion.
Having a cleaner, faster solution to declare a class would be awesome, both for dev and for teaching. That's why we all love attrs.
But we are talking here about a nice-to-have feature. Python works perfectly fine without it. But since we are at it, let's make something great.
2. Is it really that complicated? attr.s is just a normal Python function, which adds some members to a class. You don't even have to use the decorator @ syntax, that is just a convenience. To me this seems easier to teach than yet another dedicated syntax.
My guess is that, without the decorator, the attributes don't do anything. They are just declarative hints for the decorator to do the magic.
But even then, it's already an additional burden to have to explain the difference between this magic and the regular class attribute. So if I have to do it, I wish I could do it without having to explain also 2 namespaced function calls, one taking itself the return value of a call as a parameter, which BTW is a factory taking a constructor as a callback.
Do not over estimate the skills of newcommers in programming. They have a lot of things to learn.
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
On 05/17/2017 06:20 AM, Stephan Houben wrote:
class MyClass: foo = attr.ib()
MyClass = attr.s(MyClass)
Given that one of Python's great strengths is its readability, I would not use the attr library in teaching because it is not. Having a dot in the middle of words is confusing, especially when you don't already have a basis for which abbreviations are common. Is it attr.ib or att.rib or at.trib? -- ~Ethan~
On 17.05.2017 19:30, Ethan Furman wrote:
Given that one of Python's great strengths is its readability, I would not use the attr library in teaching because it is not. Having a dot in the middle of words is confusing, especially when you don't already have a basis for which abbreviations are common. Is it attr.ib or att.rib or at.trib?
It took me 5 days to see "foo = attrib()" in "foo = attr.ib()".... What the hell means "ib"? ... Sven
If this is the *only* objection to attrs let me quote some documentation: """ If playful naming turns you off, attrs comes with serious business aliases:
from attr import attrs, attrib @attrs ... class SeriousCoordinates(object): ... x = attrib() ... y = attrib() """
So attrs and attrib can be used as alternatives for attr.s and attr.ib . Personally, I like the playful names. Stephan 2017-05-17 19:39 GMT+02:00 Sven R. Kunze <srkunze@mail.de>:
On 17.05.2017 19:30, Ethan Furman wrote:
Given that one of Python's great strengths is its readability, I would not use the attr library in teaching because it is not. Having a dot in the middle of words is confusing, especially when you don't already have a basis for which abbreviations are common. Is it attr.ib or att.rib or at.trib?
It took me 5 days to see "foo = attrib()" in "foo = attr.ib()".... What the hell means "ib"? ...
Sven
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
On 17.05.2017 19:55, Stephan Houben wrote:
So attrs and attrib can be used as alternatives for attr.s and attr.ib . Personally, I like the playful names.
Ah, now I understand their documentation. :D I read this passage and thought: "where is the difference. Maybe, they meant omitting x=, y= in the constructor?" "There should be one-- and preferably only one --obvious way to do it." <<< That's why I didn't even thought there's an alternative. I feel that's a bit not-Python. ;) Sven
On Wed, May 17, 2017 at 07:39:36PM +0200, "Sven R. Kunze" <srkunze@mail.de> wrote:
It took me 5 days to see "foo = attrib()" in "foo = attr.ib()".... What the hell means "ib"? ...
Guido has named it "deadly cute". (-:
Sven
Oleg. -- Oleg Broytman http://phdru.name/ phd@phdru.name Programmers don't die, they just GOSUB without RETURN.
On 17.05.2017 13:37, Michel Desmoulin wrote:
Having a cleaner, faster solution to declare a class would be awesome, both for dev and for teaching. That's why we all love attrs.
But we are talking here about a nice-to-have feature. Python works perfectly fine without it. But since we are at it, let's make something great.
Same for me. IMHO the biggest benefit using attr is an (almost?) feature-complete and bug-free set of pre-defined __dunder__ methods as described in [1]. Defining state-variables (aka instance variables accessible via 'self.') wouldn't be enough for me to make it a valuable feature. So, one could imagine a __dunder__-method generator of some sort. But even given that (and I am only speaking for my team), I haven't even seen a use-case for namedtuples in a year. Every time we considered it, people said: "please make it its own class for documentary purposes; this thing will tend to grow faster than we can imagine".
2. Is it really that complicated? attr.s is just a normal Python function, which adds some members to a class. You don't even have to use the decorator @ syntax, that is just a convenience. To me this seems easier to teach than yet another dedicated syntax. My guess is that, without the decorator, the attributes don't do anything. They are just declarative hints for the decorator to do the magic.
But even then, it's already an additional burden to have to explain the difference between this magic and the regular class attribute.
It might also have something to do with this. IMO this feature should integrate naturally in a way that nobody notice. Sven [1] https://attrs.readthedocs.io/en/stable/why.html#hand-written-classes
Hi Sven,
But even given that (and I am only speaking for my team), I haven't even seen a use-case for namedtuples in a year. Every time we considered it, people said: "please make it its own class for documentary purposes; this thing will tend to grow faster than we can imagine".
Using namedtuple doesn't stop the class from being its "own class". Typical use case: class Foo(namedtuple("Foo", "bar "baz"), FooBase): "Foo is a very important class and you should totally use it.""" def grand_total(self): return self.bar + self.baz Stephan 2017-05-17 18:11 GMT+02:00 Sven R. Kunze <srkunze@mail.de>:
On 17.05.2017 13:37, Michel Desmoulin wrote:
Having a cleaner, faster solution to declare a class would be awesome, both for dev and for teaching. That's why we all love attrs.
But we are talking here about a nice-to-have feature. Python works perfectly fine without it. But since we are at it, let's make something great.
Same for me. IMHO the biggest benefit using attr is an (almost?) feature-complete and bug-free set of pre-defined __dunder__ methods as described in [1].
Defining state-variables (aka instance variables accessible via 'self.') wouldn't be enough for me to make it a valuable feature. So, one could imagine a __dunder__-method generator of some sort.
But even given that (and I am only speaking for my team), I haven't even seen a use-case for namedtuples in a year. Every time we considered it, people said: "please make it its own class for documentary purposes; this thing will tend to grow faster than we can imagine".
2. Is it really that complicated? attr.s is just a normal Python function, which adds some members to a class. You don't even have to use the decorator @ syntax, that is just a convenience. To me this seems easier to teach than yet another dedicated syntax.
My guess is that, without the decorator, the attributes don't do anything. They are just declarative hints for the decorator to do the magic.
But even then, it's already an additional burden to have to explain the difference between this magic and the regular class attribute.
It might also have something to do with this. IMO this feature should integrate naturally in a way that nobody notice.
Sven
[1] https://attrs.readthedocs.io/en/stable/why.html#hand-written-classes
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
On 17 May 2017 at 18:38, Stephan Houben <stephanh42@gmail.com> wrote:
Hi Sven,
But even given that (and I am only speaking for my team), I haven't even seen a use-case for namedtuples in a year. Every time we considered it, people said: "please make it its own class for documentary purposes; this thing will tend to grow faster than we can imagine".
Using namedtuple doesn't stop the class from being its "own class". Typical use case:
class Foo(namedtuple("Foo", "bar "baz"), FooBase): "Foo is a very important class and you should totally use it."""
def grand_total(self): return self.bar + self.baz
And the right (modern) way to do this is from typing import NamedTuple class Foo(NamedTuple): """Foo is a very important class and you should totally use it. """ bar: int baz: int = 0 def grand_total(self): return self.bar + self.baz typing.NamedTuple supports docstrings, user-defined methods, and default values. -- Ivan
On Wed, May 17, 2017 at 12:48 PM, Ivan Levkivskyi <levkivskyi@gmail.com> wrote:
class Foo(NamedTuple): """Foo is a very important class and you should totally use it. """ bar: int baz: int = 0
def grand_total(self): return self.bar + self.baz
Really?! I didn't know that idiom existed. It is enough for many use cases, and I was just about to require typing and pathlib on my 2.7-compatible projects. -- Juancarlo *Añez*
On 17 May 2017 at 19:40, Juancarlo Añez <apalala@gmail.com> wrote:
On Wed, May 17, 2017 at 12:48 PM, Ivan Levkivskyi <levkivskyi@gmail.com> wrote:
class Foo(NamedTuple): """Foo is a very important class and you should totally use it. """ bar: int baz: int = 0
def grand_total(self): return self.bar + self.baz
Really?!
I didn't know that idiom existed.
It is enough for many use cases, and I was just about to require typing and pathlib on my 2.7-compatible projects.
Unfortunately, this works _only_ in Python 3.6+. -- Ivan
On 05/17/2017 10:43 AM, Ivan Levkivskyi wrote:
On 17 May 2017 at 19:40, Juancarlo Añez wrote:
On Wed, May 17, 2017 at 12:48 PM, Ivan Levkivskyi wrote:
class Foo(NamedTuple): """Foo is a very important class and you should totally use it. """ bar: int baz: int = 0
def grand_total(self): return self.bar + self.baz
Really?!
I didn't know that idiom existed.
It is enough for many use cases, and I was just about to require typing and pathlib on my 2.7-compatible projects.
Unfortunately, this works _only_ in Python 3.6+.
You might want to check out the NamedTuple class from my aenum [1] library -- it is metaclass based (no execing), supports defaults, doc-strings, and other fun and possibly useful things. Here's the NamedTuple section from the docs:
Creating NamedTuples --------------------
Simple ^^^^^^
The most common way to create a new NamedTuple will be via the functional API::
>>> from aenum import NamedTuple >>> Book = NamedTuple('Book', 'title author genre', module=__name__)
This creates a ``NamedTuple`` called ``Book`` that will always contain three items, each of which is also addressable as ``title``, ``author``, or ``genre``.
``Book`` instances can be created using positional or keyword argements or a mixture of the two::
>>> b1 = Book('Lord of the Rings', 'J.R.R. Tolkien', 'fantasy') >>> b2 = Book(title='Jhereg', author='Steven Brust', genre='fantasy') >>> b3 = Book('Empire', 'Orson Scott Card', genre='scifi')
If too few or too many arguments are used a ``TypeError`` will be raised::
>>> b4 = Book('Hidden Empire') Traceback (most recent call last): ... TypeError: values not provided for field(s): author, genre >>> b5 = Book(genre='business') Traceback (most recent call last): ... TypeError: values not provided for field(s): title, author
As a ``class`` the above ``Book`` ``NamedTuple`` would look like::
>>> class Book(NamedTuple): ... title = 0 ... author = 1 ... genre = 2 ...
For compatibility with the stdlib ``namedtuple``, NamedTuple also has the ``_asdict``, ``_make``, and ``_replace`` methods, and the ``_fields`` attribute, which all function similarly::
>>> class Point(NamedTuple): ... x = 0, 'horizontal coordinate', 1 ... y = 1, 'vertical coordinate', -1 ... >>> class Color(NamedTuple): ... r = 0, 'red component', 11 ... g = 1, 'green component', 29 ... b = 2, 'blue component', 37 ... >>> Pixel = NamedTuple('Pixel', Point+Color, module=__name__) >>> pixel = Pixel(99, -101, 255, 128, 0)
>>> pixel._asdict() OrderedDict([('x', 99), ('y', -101), ('r', 255), ('g', 128), ('b', 0)])
>>> Point._make((4, 5)) Point(x=4, y=5)
>>> purple = Color(127, 0, 127) >>> mid_gray = purple._replace(g=127) >>> mid_gray Color(r=127, g=127, b=127)
>>> pixel._fields ['x', 'y', 'r', 'g', 'b']
>>> Pixel._fields ['x', 'y', 'r', 'g', 'b']
Advanced ^^^^^^^^
The simple method of creating ``NamedTuples`` requires always specifying all possible arguments when creating instances; failure to do so will raise exceptions::
>>> class Point(NamedTuple): ... x = 0 ... y = 1 ... >>> Point() Traceback (most recent call last): ... TypeError: values not provided for field(s): x, y >>> Point(1) Traceback (most recent call last): ... TypeError: values not provided for field(s): y >>> Point(y=2) Traceback (most recent call last): ... TypeError: values not provided for field(s): x
However, it is possible to specify both docstrings and default values when creating a ``NamedTuple`` using the class method::
>>> class Point(NamedTuple): ... x = 0, 'horizontal coordinate', 0 ... y = 1, 'vertical coordinate', 0 ... >>> Point() Point(x=0, y=0) >>> Point(1) Point(x=1, y=0) >>> Point(y=2) Point(x=0, y=2)
It is also possible to create ``NamedTuples`` that only have named attributes for certain fields; any fields without names can still be accessed by index::
>>> class Person(NamedTuple): ... fullname = 2 ... phone = 5 ... >>> p = Person('Ethan', 'Furman', 'Ethan Furman', ... 'ethan at stoneleaf dot us', ... 'ethan.furman', '999.555.1212') >>> p Person('Ethan', 'Furman', 'Ethan Furman', 'ethan at stoneleaf dot us', 'ethan.furman', '999.555.1212') >>> p.fullname 'Ethan Furman' >>> p.phone '999.555.1212' >>> p[0] 'Ethan'
In the above example the last named field was also the last field possible; in those cases where you don't need to have the last possible field named, you can provide a ``_size_`` of ``TupleSize.minimum`` to declare that more fields are okay::
>>> from aenum import TupleSize >>> class Person(NamedTuple): ... _size_ = TupleSize.minimum ... first = 0 ... last = 1 ...
or, optionally if using Python 3::
>>> class Person(NamedTuple, size=TupleSize.minimum): # doctest: +SKIP ... first = 0 ... last = 1
and in use::
>>> Person('Ethan', 'Furman') Person(first='Ethan', last='Furman')
>>> Person('Ethan', 'Furman', 'ethan.furman') Person('Ethan', 'Furman', 'ethan.furman')
>>> Person('Ethan', 'Furman', 'ethan.furman', 'yay Python!') Person('Ethan', 'Furman', 'ethan.furman', 'yay Python!')
>>> Person('Ethan') Traceback (most recent call last): ... TypeError: values not provided for field(s): last
Also, for those cases where even named fields may not be present, you can specify ``TupleSize.variable``::
>>> class Person(NamedTuple): ... _size_ = TupleSize.variable ... first = 0 ... last = 1 ...
>>> Person('Ethan') Person('Ethan')
>>> Person(last='Furman') Traceback (most recent call last): ... TypeError: values not provided for field(s): first
Creating new ``NamedTuples`` from existing ``NamedTuples`` is simple::
>>> Point = NamedTuple('Point', 'x y') >>> Color = NamedTuple('Color', 'r g b') >>> Pixel = NamedTuple('Pixel', Point+Color, module=__name__) >>> Pixel <NamedTuple 'Pixel'>
The existing fields in the bases classes are renumbered to fit the new class, but keep their doc strings and default values. If you use standard subclassing::
>>> Point = NamedTuple('Point', 'x y') >>> class Pixel(Point): ... r = 2, 'red component', 11 ... g = 3, 'green component', 29 ... b = 4, 'blue component', 37 ... >>> Pixel.__fields__ ['x', 'y', 'r', 'g', 'b']
You must manage the numbering yourself.
-- ~Ethan~
On 05/17/2017 02:06 PM, Ethan Furman wrote:
You might want to check out the NamedTuple class from my aenum [1] library
[1] https://pypi.python.org/pypi/aenum -- ~Ethan~
Hi Stephan, hi Ivan, On 17.05.2017 18:48, Ivan Levkivskyi wrote:
from typing import NamedTuple
class Foo(NamedTuple): """Foo is a very important class and you should totally use it. """ bar: int baz: int = 0
def grand_total(self): return self.bar + self.baz
typing.NamedTuple supports docstrings, user-defined methods, and default values.
I hope the second ': int' can be omitted because 0 already is an int. This makes me wonder three things: 1) Michel, can newcomers differentiate between when to use ' : ' and when to use ' = ' and a combination thereof? 2) There must be a lot of cornercases where people rewrite Foo to be a normal class in the end, right? 3) If one doesn't need tuple-__dunder__ methods, a "normal" class would even need 1 line less. (+ Stephan's second point) So, this still leaves those missing __dunder__ magic methods doing the right thing at the right time. Sven
Hi Sven, "I hope the second ': int' can be omitted because 0 already is an int." 0 is also an Any, an object, a SupportAbs, and a Union[int, str]. And infinitely more, of course. A typechecker needs to be explicitly told which was intended. Stephan Op 17 mei 2017 19:52 schreef "Sven R. Kunze" <srkunze@mail.de>: Hi Stephan, hi Ivan, On 17.05.2017 18:48, Ivan Levkivskyi wrote: from typing import NamedTuple class Foo(NamedTuple): """Foo is a very important class and you should totally use it. """ bar: int baz: int = 0 def grand_total(self): return self.bar + self.baz typing.NamedTuple supports docstrings, user-defined methods, and default values. I hope the second ': int' can be omitted because 0 already is an int. This makes me wonder three things: 1) Michel, can newcomers differentiate between when to use ' : ' and when to use ' = ' and a combination thereof? 2) There must be a lot of cornercases where people rewrite Foo to be a normal class in the end, right? 3) If one doesn't need tuple-__dunder__ methods, a "normal" class would even need 1 line less. (+ Stephan's second point) So, this still leaves those missing __dunder__ magic methods doing the right thing at the right time. Sven
On May 17, 2017 04:16, "Michel Desmoulin" <desmoulinmichel@gmail.com> wrote: Le 17/05/2017 à 07:22, Guido van Rossum a écrit :
On Tue, May 16, 2017 at 8:14 PM, Juancarlo Añez <apalala@gmail.com <mailto:apalala@gmail.com>> wrote:
What I like about attrs is:
* The class level declaration of instance attributes * That the reasonable *init*, *repr*, and *eq* are generated
OK, the former should be doable using PEP 526 (the type is stored in __annotations__ and the default in the class dict given to the metaclass), while the latter should be doable using a standard metaclass -- assuming we can agree on what the "reasonable" __init__, __repr__ and __eq__ should do.
I don’t like the excessive wordiness in attrs,
Really? @attr.s is wordy? :-) I think it's deadly cute. (The only library I've ever seen that did something worse was "monocle" which used @_o.)
import attr @attr.s ... class Point: ... x = attr.ib(default=42) ... y = attr.ib(default=attr.Factory(list))
Is pretty wordy compared to something like another language would do such as: class Point: int x = 42 list y = [] Now I get that: - Python already has a similar syntax creating class attributes. - You can't put mutable objects here. - attr does more since it generates dunder methods. But having an import, a decorator and verbose calls to attr.ib does not feel like idiomatic Python at all. Python is an elegant and expressive language. This is none of the above. Also Python is beginner friendly. Now OPP is already hard to teach to my students, but if I have to add this to the mix, I will just have to tell them to copy / paste it blindly for a long time before I can get to the point they can understand what it does. We should be able to find a middle ground. First, if we have something LIKE attr, should it need an import? Basic data structures may not require an import to work. async/await are way better than import @asyncio.coroutine. Secondly, while I really, really, really want this feature, I think we should not rush it. Some ideas have to be explored. E.G: Adding keywords for it ? I know adding a keyword is the worst thing one can suggest on this list ever. But it has to be mentioned because most other languages do it this way. class Point: instancevar x = 42 instancevar y = lazy [] # we add a debate about this a few months ago Adding a special syntax for it ? ruby has something similar. class Point: @x = 42 @@y = list Upgrading the class constructor? It does not address the mutablility issue though. class Point(x=42, y=[]) Mixing concepts? class Point(metaclass=autoclass(x=42, y=lazy [])): pass @args(x=42) @factory_args(y=list) @autodunder() class Point: pass @autoclass( x=42 y=autoclass.lazy(list) ) class Point: pass Just adding attrs, which is a workaround to a missing feature in Python, as a boiler plate and calling it a day seems unwise. Don't get me wrong, I like attrs, I like asyncio and I like the whole battery included concept. But we lived without it until now, so let's not go too fast on this. What about an attribute-level decorator-like syntax, like: class Point: @instattr x = 42 @instattr @lazy y = [] Or: class Point: @instattr: x = 42 @instattr: @lazy: y = [] This would have the benefit of a keyword-like syntax without actually needing a new keyword. The question is how such a system would work in a general manner. Of course these aren't decorators, something new would be needed. I see two main approaches. 1. These functions are called both at class creation and initialization time with arguments that let it decide what to do. So perhaps it is given whatever is on the right side of the "=", the class, and the class instance (which is None at class creation time). Perhaps it is given the variable name as a string, or perhaps Python magically adds the name to the namespace whenever the function returns. Alternatively, this could be a class and there are two dunder methods that are called at class creation time and class initialization time. 2. These are called at attribute access time. Python sets up the attribute as a special property, and whenever the attribute is accessed the function is given arguments that allow it to infer what is going on. It then controls if and how the variable is accessed. Alternatively, this could be a class and there are dunder methods called at various times. The second approach would be more flexible, while the first approach would require less work for developers for basic tasks. The two approaches are not mutually-exclusive, either, especially if a dunder method-based approach is used. I know that strictly speaking the property-based approach could be implemented in the function-based approach, but that would be a lot more work.
On 17 May 2017 at 13:14, Juancarlo Añez <apalala@gmail.com> wrote:
On Tue, May 16, 2017 at 5:04 PM, Guido van Rossum <gvanrossum@gmail.com> wrote: What I like about attrs is:
* The class level declaration of instance attributes * That the reasonable init, repr, and eq are generated
These are also the two main benefits for my own use cases, with easy conversion to JSON compatible dicts being third. I'm less bothered by the wordiness, hence my suggestion for borrowing the attrs API design and doing this as a standard library module along the lines of: from autoclass import data_record, field @data_record class Point3D: x: int = field(default=0) y: int = field(default=0) z: int = field(default=0) While that's wordier than dedicated syntax in the simple cases, it also means that - if we want to define additional templates in the future, it just means adding a new decorator to the autoclass module - things like ORMs and other class based schema DSLs are a natural extension of this "runtime class template" model, - class level settings (e.g. declaring post-init immutability) are just keyword arguments to a function call - field level settings (e.g. omitting the field from the generated repr) are just keyword arguments to a function call Those last two points echo the primary reason that print was converted from a statement to a builtin in Python 3 That said, even with this model, the base case of "fields with an immutable or shared default" could potentially be simplified to: from autoclass import data_record @data_record class Point3D: x: int = 0 y: int = 0 z: int = 0 However, the potentially surprising behaviour there is that to implement it, the decorator not only has to special case the output of "field()" calls, but also has to special case any object that implements the descriptor protocol to avoid getting confused by normal method and property definitions. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia
Hi Guido, As mentioned I am a heavy user of namedtuple, I use it everywhere where constructor arguments are equal to instance variables. Which is quite often, at least in my programming style (possibly under the influence of ML's "datatype" and Scala's "case class"-es.) Compared to namedtuple, I see that attr solves a number of issues which sometimes prevented me from using namedtuple: 1. Allow hash and equality to be based on object identity, rather than structural identity, this is very important if one wants to store un-hashable objects in the instance. (In my case: mostly dict's and numpy arrays). 2. Not subclassed from tuple. I have been bitten by this subclassing when trying to set up singledispatch on sequences and also on my classes. 3. Easily allow to specify default values. With namedtuple this requires overriding __new__. 4. Easily allow to specify a conversion function. For example I have some code like below: note that I can store a numpy array while keeping hashability and I can make it convert to a numpy array in the constructor. @attr.s(cmp=False, hash=False) class SvgTransform(SvgPicture): child = attr.ib() matrix = attr.ib(convert=numpy.asarray) These are the main advantages I have encountered so far. Stephan 2017-05-16 23:04 GMT+02:00 Guido van Rossum <gvanrossum@gmail.com>:
Stephen,
What features of attrs specifically solve your use cases?
--Guido
On Tue, May 16, 2017 at 12:18 PM, Stephan Houben <stephanh42@gmail.com> wrote:
Hi all,
Thanks to this thread I learned about the "attrs" library. I am a heavy namedtuple (ab)user but I think I will be using attrs going forward.
If something like attrs would made it in the standard library it would be awesome.
Thanks,
Stephan
2017-05-16 20:08 GMT+02:00 Brett Cannon <brett@python.org>:
Maybe we can bring this up as a lightning talk at the language summit to see who in the room has the appropriate background knowledge? And obviously someone can talk to Hynek to see if he wants to provide input based on community feedback for attrs and lessons learned.
On Tue, 16 May 2017 at 08:11 Guido van Rossum <gvanrossum@gmail.com> wrote:
Maybe Lukasz is interested?
On May 16, 2017 8:00 AM, "Chris Angelico" <rosuav@gmail.com> wrote:
On Wed, May 17, 2017 at 12:53 AM, Guido van Rossum <guido@python.org> wrote:
I could also try this myself in my spare time at PyCon (surprisingly, I have some!). It sounds kind of interesting. However I've never used the 'attrs' package...
Me neither, so I'm not really an ideal person to head this up. Is there anyone who (a) knows what is and isn't Pythonic, (b) has used 'attrs', and (c) has spare time? It's not an easy trifecta but we can hope!
ChrisA _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
-- --Guido van Rossum (python.org/~guido)
Hi Stephan, On 17.05.2017 08:49, Stephan Houben wrote:
2. Not subclassed from tuple. I have been bitten by this subclassing when trying to set up singledispatch on sequences and also on my classes.
Would it make sense to have a 'simpleobject'? Which basically implements a NamedTuple constructor but nothing more? class Foo(simpleobject): attribute1: User attribute2: Blog attribute3: list And if you need more __dunder__ magic, have it provided by some mixins? class Foo(dictlike, tuplelike, simpleobject): attribute1: User attribute2: Blog attribute3: list def __my_dunder__(self): ... I don't know exactly if some of those dictlike, tuplelike mixins are already available in the stdlib under a different name, but at least to me this looks like plug'n'play __dunder__ magic. Sven
On 17 May 2017 at 20:09, Sven R. Kunze <srkunze@mail.de> wrote:
Hi Stephan,
On 17.05.2017 08:49, Stephan Houben wrote:
2. Not subclassed from tuple. I have been bitten by this subclassing when trying to set up singledispatch on sequences and also on my classes.
Would it make sense to have a 'simpleobject'? Which basically implements a NamedTuple constructor but nothing more?
class Foo(simpleobject): attribute1: User attribute2: Blog attribute3: list
And if you need more __dunder__ magic, have it provided by some mixins?
class Foo(dictlike, tuplelike, simpleobject): attribute1: User attribute2: Blog attribute3: list
def __my_dunder__(self): ...
As I understand this is more or less what is proposed, the idea is to write it into a PEP and consider API/corner cases/implementation/etc. -- Ivan
On 17.05.2017 23:29, Ivan Levkivskyi wrote:
On 17 May 2017 at 20:09, Sven R. Kunze <srkunze@mail.de <mailto:srkunze@mail.de>> wrote:
class Foo(dictlike, tuplelike, simpleobject): attribute1: User attribute2: Blog attribute3: list
def __my_dunder__(self): ...
As I understand this is more or less what is proposed,
Are you sure? Could you point me to the relevant messages where mixins are mentioned as a key part of the proposal? All I could find are message using the @decorator syntaxes. We've been working with mixins successfully for years now and I can tell you that it's "just" a clever way of refactoring existing code in order to make it more accessible to other modules *based on use-cases*. So, the best person to tell what pieces to factor out would be Stephan using his 4-point list. And of course other people using NamedTuple but frequently refactoring to "attr" or own class because NamedTuple just is too much (defines too much implicitly). Another benefit is that NamedTuple itself would become a mere set of base class and mixins.
the idea is to write it into a PEP and consider API/corner cases/implementation/etc.
Who's writing it? Regards, Sven
On 5/18/17 2:26 PM, Sven R. Kunze wrote:
On 17.05.2017 23:29, Ivan Levkivskyi wrote:
the idea is to write it into a PEP and consider API/corner cases/implementation/etc.
Who's writing it?
Guido, Hynek, and I met today. I'm writing up our notes, and hopefully that will eventually become a PEP. I'm going to propose calling this feature "Data Classes" as a placeholder until we come up with something better. Once I have something readable, I'll open it up for discussion. Eric.
On 19.05.2017 04:37, Eric V. Smith wrote:
On 5/18/17 2:26 PM, Sven R. Kunze wrote:
On 17.05.2017 23:29, Ivan Levkivskyi wrote:
the idea is to write it into a PEP and consider API/corner cases/implementation/etc.
Who's writing it?
Guido, Hynek, and I met today. I'm writing up our notes, and hopefully that will eventually become a PEP. I'm going to propose calling this feature "Data Classes" as a placeholder until we come up with something better.
FWIIW: Kotlin has similar feature - data class. data class ABCthing(val a: Double, val b: Double, val c: Double) Niki
On Thu, May 18, 2017 at 6:38 PM Eric V. Smith <eric@trueblade.com> wrote:
On 5/18/17 2:26 PM, Sven R. Kunze wrote:
On 17.05.2017 23:29, Ivan Levkivskyi wrote:
the idea is to write it into a PEP and consider API/corner cases/implementation/etc.
Who's writing it?
Guido, Hynek, and I met today. I'm writing up our notes, and hopefully that will eventually become a PEP. I'm going to propose calling this feature "Data Classes" as a placeholder until we come up with something better.
Once I have something readable, I'll open it up for discussion.
Did anything PEP-ish ever come out of this? -gps
On Jul 12, 2017, at 9:02 PM, Gregory P. Smith <greg@krypto.org> wrote:
On Thu, May 18, 2017 at 6:38 PM Eric V. Smith <eric@trueblade.com> wrote: On 5/18/17 2:26 PM, Sven R. Kunze wrote:
On 17.05.2017 23:29, Ivan Levkivskyi wrote:
the idea is to write it into a PEP and consider API/corner cases/implementation/etc.
Who's writing it?
Guido, Hynek, and I met today. I'm writing up our notes, and hopefully that will eventually become a PEP. I'm going to propose calling this feature "Data Classes" as a placeholder until we come up with something better.
Once I have something readable, I'll open it up for discussion.
Did anything PEP-ish ever come out of this?
-gps
The real world has intruded on my "Data Classes" time. But when I can, I'll get back to it and complete it. Hopefully I can spend all of the Core Sprint time on it. Eric.
On 13 July 2017 at 11:10, Eric V. Smith <eric@trueblade.com> wrote:
The real world has intruded on my "Data Classes" time. But when I can, I'll get back to it and complete it. Hopefully I can spend all of the Core Sprint time on it.
One of the presentations at this year's PyCon Australia Education Seminar is on teaching OOP concepts with Python [1], so I'll make sure to bring this topic up with Bruce (the presenter) and other attendees. While https://github.com/ericvsmith/dataclasses/blob/master/pep-xxxx.rst is clearly still a work in progress, there's enough there for me to at least collect some first impressions :) Cheers, Nick. [1] https://2017.pycon-au.org/schedule/presentation/94/ -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia
I hope it will happen. https://github.com/ericvsmith/dataclasses On Wed, Jul 12, 2017 at 6:02 PM, Gregory P. Smith <greg@krypto.org> wrote:
On Thu, May 18, 2017 at 6:38 PM Eric V. Smith <eric@trueblade.com> wrote:
On 5/18/17 2:26 PM, Sven R. Kunze wrote:
On 17.05.2017 23:29, Ivan Levkivskyi wrote:
the idea is to write it into a PEP and consider API/corner cases/implementation/etc.
Who's writing it?
Guido, Hynek, and I met today. I'm writing up our notes, and hopefully that will eventually become a PEP. I'm going to propose calling this feature "Data Classes" as a placeholder until we come up with something better.
Once I have something readable, I'll open it up for discussion.
Did anything PEP-ish ever come out of this?
-gps
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
-- --Guido van Rossum (python.org/~guido)
On 5/18/17 2:26 PM, Sven R. Kunze wrote:
On 17.05.2017 23:29, Ivan Levkivskyi wrote:
On 17 May 2017 at 20:09, Sven R. Kunze <srkunze@mail.de <mailto:srkunze@mail.de>> wrote:
class Foo(dictlike, tuplelike, simpleobject): attribute1: User attribute2: Blog attribute3: list
def __my_dunder__(self): ...
As I understand this is more or less what is proposed,
Are you sure? Could you point me to the relevant messages where mixins are mentioned as a key part of the proposal? All I could find are message using the @decorator syntaxes.
We've been working with mixins successfully for years now and I can tell you that it's "just" a clever way of refactoring existing code in order to make it more accessible to other modules *based on use-cases*.
So, the best person to tell what pieces to factor out would be Stephan using his 4-point list. And of course other people using NamedTuple but frequently refactoring to "attr" or own class because NamedTuple just is too much (defines too much implicitly).
Could you point me to this 4-point list of Stephan's? I couldn't find anything in the archive that you might be referring to. Eric.
Could you point me to this 4-point list of Stephan's? I couldn't find anything in the archive that you might be referring to.
Never mind, I found them here: https://mail.python.org/pipermail/python-ideas/2017-May/045679.html Eric.
Exactly this. On 19.05.2017 14:42, Eric V. Smith wrote:
Could you point me to this 4-point list of Stephan's? I couldn't find anything in the archive that you might be referring to.
Never mind, I found them here: https://mail.python.org/pipermail/python-ideas/2017-May/045679.html
Eric.
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
For people who don't want to click on links: 1. Allow hash and equality to be based on object identity, rather than structural identity, this is very important if one wants to store un-hashable objects in the instance. (In my case: mostly dict's and numpy arrays). 2. Not subclassed from tuple. I have been bitten by this subclassing when trying to set up singledispatch on sequences and also on my classes. 3. Easily allow to specify default values. With namedtuple this requires overriding __new__. 4. Easily allow to specify a conversion function. For example I have some code like below: note that I can store a numpy array while keeping hashability and I can make it convert to a numpy array in the constructor. @attr.s(cmp=False, hash=False) class SvgTransform(SvgPicture): child = attr.ib() matrix = attr.ib(convert=numpy.asarray) I have one question about (4) -- how and when is the conversion function used, and what is its signature? On Fri, May 19, 2017 at 5:42 AM, Eric V. Smith <eric@trueblade.com> wrote:
Could you point me to this 4-point list of Stephan's? I couldn't find
anything in the archive that you might be referring to.
Never mind, I found them here: https://mail.python.org/pipermail/python-ideas/2017-May/045679.html
Eric.
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
-- --Guido van Rossum (python.org/~guido)
Let me quote the attrs docs: "" convert (callable) – callable() that is called by attrs-generated __init__ methods to convert attribute’s value to the desired format. It is given the passed-in value, and the returned value will be used as the new value of the attribute. The value is converted before being passed to the validator, if any. """ So the signature is essentially: self.myattrib = callable (myattrib) Stephan Op 19 mei 2017 20:25 schreef "Guido van Rossum" <guido@python.org>:
For people who don't want to click on links:
1. Allow hash and equality to be based on object identity, rather than structural identity, this is very important if one wants to store un-hashable objects in the instance. (In my case: mostly dict's and numpy arrays).
2. Not subclassed from tuple. I have been bitten by this subclassing when trying to set up singledispatch on sequences and also on my classes.
3. Easily allow to specify default values. With namedtuple this requires overriding __new__.
4. Easily allow to specify a conversion function. For example I have some code like below: note that I can store a numpy array while keeping hashability and I can make it convert to a numpy array in the constructor.
@attr.s(cmp=False, hash=False) class SvgTransform(SvgPicture): child = attr.ib() matrix = attr.ib(convert=numpy.asarray)
I have one question about (4) -- how and when is the conversion function used, and what is its signature?
On Fri, May 19, 2017 at 5:42 AM, Eric V. Smith <eric@trueblade.com> wrote:
Could you point me to this 4-point list of Stephan's? I couldn't find
anything in the archive that you might be referring to.
Never mind, I found them here: https://mail.python.org/pipermail/python-ideas/2017-May/045679.html
Eric.
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
-- --Guido van Rossum (python.org/~guido)
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
So it is only called by __init__ and not by __setattr__? On Fri, May 19, 2017 at 11:32 AM, Stephan Houben <stephanh42@gmail.com> wrote:
Let me quote the attrs docs:
"" convert (callable) – callable() that is called by attrs-generated __init__ methods to convert attribute’s value to the desired format. It is given the passed-in value, and the returned value will be used as the new value of the attribute. The value is converted before being passed to the validator, if any. """
So the signature is essentially:
self.myattrib = callable (myattrib)
Stephan
Op 19 mei 2017 20:25 schreef "Guido van Rossum" <guido@python.org>:
For people who don't want to click on links:
1. Allow hash and equality to be based on object identity, rather than structural identity, this is very important if one wants to store un-hashable objects in the instance. (In my case: mostly dict's and numpy arrays).
2. Not subclassed from tuple. I have been bitten by this subclassing when trying to set up singledispatch on sequences and also on my classes.
3. Easily allow to specify default values. With namedtuple this requires overriding __new__.
4. Easily allow to specify a conversion function. For example I have some code like below: note that I can store a numpy array while keeping hashability and I can make it convert to a numpy array in the constructor.
@attr.s(cmp=False, hash=False) class SvgTransform(SvgPicture): child = attr.ib() matrix = attr.ib(convert=numpy.asarray)
I have one question about (4) -- how and when is the conversion function used, and what is its signature?
On Fri, May 19, 2017 at 5:42 AM, Eric V. Smith <eric@trueblade.com> wrote:
Could you point me to this 4-point list of Stephan's? I couldn't find
anything in the archive that you might be referring to.
Never mind, I found them here: https://mail.python.org/pipermail/python-ideas/2017-May/045679.html
Eric.
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
-- --Guido van Rossum (python.org/~guido)
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
-- --Guido van Rossum (python.org/~guido)
Hi Guido, Yes indeed, *only* invoked by __init__ . See my test below. ===== import attr @attr.s class Foo: x = attr.ib(convert=str) foo = Foo(42) print(repr(foo.x)) # prints '42' foo.x = 42 print(repr(foo.x)) # prints 42 ====== Not sure if this is a good design but it matches the docs. Stephan Op 19 mei 2017 20:36 schreef "Guido van Rossum" <guido@python.org>: So it is only called by __init__ and not by __setattr__? On Fri, May 19, 2017 at 11:32 AM, Stephan Houben <stephanh42@gmail.com> wrote:
Let me quote the attrs docs:
"" convert (callable) – callable() that is called by attrs-generated __init__ methods to convert attribute’s value to the desired format. It is given the passed-in value, and the returned value will be used as the new value of the attribute. The value is converted before being passed to the validator, if any. """
So the signature is essentially:
self.myattrib = callable (myattrib)
Stephan
Op 19 mei 2017 20:25 schreef "Guido van Rossum" <guido@python.org>:
For people who don't want to click on links:
1. Allow hash and equality to be based on object identity, rather than structural identity, this is very important if one wants to store un-hashable objects in the instance. (In my case: mostly dict's and numpy arrays).
2. Not subclassed from tuple. I have been bitten by this subclassing when trying to set up singledispatch on sequences and also on my classes.
3. Easily allow to specify default values. With namedtuple this requires overriding __new__.
4. Easily allow to specify a conversion function. For example I have some code like below: note that I can store a numpy array while keeping hashability and I can make it convert to a numpy array in the constructor.
@attr.s(cmp=False, hash=False) class SvgTransform(SvgPicture): child = attr.ib() matrix = attr.ib(convert=numpy.asarray)
I have one question about (4) -- how and when is the conversion function used, and what is its signature?
On Fri, May 19, 2017 at 5:42 AM, Eric V. Smith <eric@trueblade.com> wrote:
Could you point me to this 4-point list of Stephan's? I couldn't find
anything in the archive that you might be referring to.
Never mind, I found them here: https://mail.python.org/pipermail/python-ideas/2017-May/045679.html
Eric.
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
-- --Guido van Rossum (python.org/~guido)
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
-- --Guido van Rossum (python.org/~guido)
On Fri, May 19, 2017 at 11:24:53AM -0700, Guido van Rossum wrote:
4. Easily allow to specify a conversion function. For example I have some code like below: note that I can store a numpy array while keeping hashability and I can make it convert to a numpy array in the constructor.
@attr.s(cmp=False, hash=False) class SvgTransform(SvgPicture): child = attr.ib() matrix = attr.ib(convert=numpy.asarray)
I find that completely enigmatic, there's far too much implicit behaviour going on behind the scenes. I couldn't even begin to guess what SvgTransform as a class does, or what SvgTransform.child and SvgTransform.matrix are. I suppose that's okay for experts to whom the attrs module is second nature, but I think this approach is far too "magical" for my tastes. Instead of trying to cover every possible use-case from a single decorator with a multitude of keyword arguments, I think covering the simple cases is enough. Explicitly overriding methods is not a bad thing! It is much more comprehensible to see an explicit class with methods than a decorator with multiple keyword arguments and callbacks. I like the namedtuple approach: I think it hits the sweet spot between "having to do everything by hand" and "everything is magical". -- Steve
On 20 May 2017 at 10:19, Steven D'Aprano <steve@pearwood.info> wrote:
On Fri, May 19, 2017 at 11:24:53AM -0700, Guido van Rossum wrote:
4. Easily allow to specify a conversion function. For example I have some code like below: note that I can store a numpy array while keeping hashability and I can make it convert to a numpy array in the constructor.
@attr.s(cmp=False, hash=False) class SvgTransform(SvgPicture): child = attr.ib() matrix = attr.ib(convert=numpy.asarray)
I find that completely enigmatic, there's far too much implicit behaviour going on behind the scenes. I couldn't even begin to guess what SvgTransform as a class does, or what SvgTransform.child and SvgTransform.matrix are.
I suppose that's okay for experts to whom the attrs module is second nature, but I think this approach is far too "magical" for my tastes.
Some of the key problems I personally see are that attrs reuses a general noun (attributes) rather than using other words that are more evocative of the "data record" use case, and many of the parameter names are about "How attrs work" and "How Python magic methods work" rather than "Behaviours I would like this class to have". That's fine for someone that's already comfortable writing those behaviours by hand and just wants to automate the boilerplate away (which is exactly the problem that attrs was written to solve), but it's significantly more problematic once we assume people will be using a feature like this *before* learning how to write out all the corresponding boilerplate themselves (which is the key additional complication that a language level version of this will have to account for). However, consider instead the following API sketch: from autoclass import data_record, data_field @data_record(orderable=False, hashable=False) class SvgTransform(SvgPicture): child = data_field() matrix = data_field(setter=numpy.asarray) Here, the core concepts to be learned would be: - the "autoclass" module lets you ask the interpreter to automatically fill in class details - SvgTransform is a data record that cannot be hashed, and cannot be ordered - it is a Python class inheriting from SvgPicture - it has two defined fields, child & matrix - we know "child" is an ordinary read/write instance attribute - we know "matrix" is a property, using numpy.asarray as its setter In this particular API sketch, data_record is just a class decorator factory, and data_field is a declarative helper type for use with that factory, so if you wanted to factor out particular combinations, you'd just write ordinary helper functions.
Instead of trying to cover every possible use-case from a single decorator with a multitude of keyword arguments, I think covering the simple cases is enough. Explicitly overriding methods is not a bad thing! It is much more comprehensible to see an explicit class with methods than a decorator with multiple keyword arguments and callbacks.
This isn't the case for folks that have to actually *read* dunder methods to find out what a class does, thought. Reading an imperatively defined class only works that way once you're able to mentally pattern match "Oh, that's a conventional __init__, that's a conventional __repr__, that's a conventional __hash__, that's a conventional __eq__, that's a conventional __lt__ implementation, etc, etc". Right now, telling Python "I want to do the same stock-standard things that everyone always does" means writing a lot of repetitive logic (or, more likely, copying the logic from an existing class that you or someone else wrote, and editing it to fit). The idea behind offering some form of declarative class definitions is to build out a vocabulary of conventional class behaviours, and make that vocabulary executable such that folks can use it to write applications even if they haven't learned how it works under the hood yet. As with descriptors before it, that vocabulary may also take advantage of the fact that Python offers first class functions to allow callbacks and transformation functions to be injected at various steps in the process *without* requiring you to also spell out all the other steps in the process that you don't want to alter.
I like the namedtuple approach: I think it hits the sweet spot between "having to do everything by hand" and "everything is magical".
It's certainly a lot better than nothing at all, but it brings a lot of baggage with it due to the fact that it *is* a tuple. Declarative class definitions aim to offer the convenience of namedtuple definitions, without the complications that arise from the "it's a tuple with some additional metadata and behaviours" aspects. Database object-relational-mapping layers like those in SQL Alchemy and Django would be the most famous precursors for this, but there are also things like Django Form definitions, and APIs like JSL (which uses Python classes to declaratively define JSON Schema documents). For folks already familiar with ORMs, declarative classes are just a matter of making in memory data structures as easy to work with as database backed ones. For folks that *aren't* familiar with ORMs yet, then declarative classes provide a potentially smoother learning curve, since the "declarative class" aspects can be better separated from the "object-relational mapping" aspects. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia
On 20.05.2017 18:06, Nick Coghlan wrote:
That's fine for someone that's already comfortable writing those behaviours by hand and just wants to automate the boilerplate away (which is exactly the problem that attrs was written to solve), but it's significantly more problematic once we assume people will be using a feature like this *before* learning how to write out all the corresponding boilerplate themselves (which is the key additional complication that a language level version of this will have to account for).
That's a good point. At least, Python does a very good job at reducing boilerplate in many cases in the first case. One usually starts small instead of big.
[API sketch]
In this particular API sketch, data_record is just a class decorator factory, and data_field is a declarative helper type for use with that factory, so if you wanted to factor out particular combinations, you'd just write ordinary helper functions.
I might weigh in that decorating classes seems to be a bit awkward. Especially because I know that there are many people and frameworks out there telling you to decorate your classes with their decorators. If you don't pay enough attention, you end up having something like this: @nice_decoration(para1, para2) @decorate_somehow @important_decoration(para3) @please_use_me() class MyClass(Mixin2, Mixin1, BaseClass): para4 = 'foo' para5 = 'bar' para6 = 'boo' para7 = 'baz' So, there's a region with decorations (decorators + params) OUTSIDE the class and there's a region with declarations (mixins + params) INSIDE the class; BOTH doing some sort of configuration of the class. I honestly cannot tell which API style would be better but it seems we (at least internally) decided for the latter version: "If it belongs to the class, it should be inside the class." Maybe, Python standard declarative class construction could be an exception because it's the default. But I am not sure. Technically, I think, both approaches can achieve the same result.
Database object-relational-mapping layers like those in SQL Alchemy and Django would be the most famous precursors for this, but there are also things like Django Form definitions, and APIs like JSL (which uses Python classes to declaratively define JSON Schema documents).
For folks already familiar with ORMs, declarative classes are just a matter of making in memory data structures as easy to work with as database backed ones. For folks that *aren't* familiar with ORMs yet, then declarative classes provide a potentially smoother learning curve, since the "declarative class" aspects can be better separated from the "object-relational mapping" aspects.
Well said. Regards, Sven
Just one additional word about mixins: we've been inspired by Django's extensive usage of mixins for their class-based views, forms and model classes. For one, we defined more mixins for those classes (like UrlMixin for view classes), and for another we developed our own mixin infrastructure for domain-specific classes. In the end, a lot of our top-level classes (the ones actually used as API etc.) look like plug'n'play + configuration: class MyImportantThing(Mixin1, Mixin2, Base): url = 'blaa/asdf' next_foo = 234 special_model = User For example in this case, "url" might be the config parameter for "Mixin1", "next_foo" for "Mixin2" and "special_model" is required/recognized by "Base". As you can imagine, testing is also easier. Regards, Sven On 19.05.2017 06:31, Eric V. Smith wrote:
On 5/18/17 2:26 PM, Sven R. Kunze wrote:
On 17.05.2017 23:29, Ivan Levkivskyi wrote:
On 17 May 2017 at 20:09, Sven R. Kunze <srkunze@mail.de <mailto:srkunze@mail.de>> wrote:
class Foo(dictlike, tuplelike, simpleobject): attribute1: User attribute2: Blog attribute3: list
def __my_dunder__(self): ...
As I understand this is more or less what is proposed,
Are you sure? Could you point me to the relevant messages where mixins are mentioned as a key part of the proposal? All I could find are message using the @decorator syntaxes.
We've been working with mixins successfully for years now and I can tell you that it's "just" a clever way of refactoring existing code in order to make it more accessible to other modules *based on use-cases*.
So, the best person to tell what pieces to factor out would be Stephan using his 4-point list. And of course other people using NamedTuple but frequently refactoring to "attr" or own class because NamedTuple just is too much (defines too much implicitly).
Could you point me to this 4-point list of Stephan's? I couldn't find anything in the archive that you might be referring to.
Eric.
(Not Stephen either). I've been using attrs for some time now, but only superficially. I'm not sure yet if I want to make it mandatory for my team or not. My biggest issues so far are: - how to truly leverage it with a declarative ORM? (SQLAlchemy in my case, the workaround being to only use a subset of attrs' functionalities, at the expense of additional complexity). - how to make it interop with type annotations? (Which is also an issue for SQLAlchemy, AFAIK, at this point). I won't be in Pycon (but I will be at PyParis next month, obviously, since I'm organising it ;). Hynek will be there, from what I see so obviously if a PEP or some fresh ideas emerge from the discussions there, I'll be more than happy. Have a nice Pycon. S. On Tue, May 16, 2017 at 11:04 PM, Guido van Rossum <gvanrossum@gmail.com> wrote:
Stephen,
What features of attrs specifically solve your use cases?
--Guido
On Tue, May 16, 2017 at 12:18 PM, Stephan Houben <stephanh42@gmail.com> wrote:
Hi all,
Thanks to this thread I learned about the "attrs" library. I am a heavy namedtuple (ab)user but I think I will be using attrs going forward.
If something like attrs would made it in the standard library it would be awesome.
Thanks,
Stephan
2017-05-16 20:08 GMT+02:00 Brett Cannon <brett@python.org>:
Maybe we can bring this up as a lightning talk at the language summit to see who in the room has the appropriate background knowledge? And obviously someone can talk to Hynek to see if he wants to provide input based on community feedback for attrs and lessons learned.
On Tue, 16 May 2017 at 08:11 Guido van Rossum <gvanrossum@gmail.com> wrote:
Maybe Lukasz is interested?
On May 16, 2017 8:00 AM, "Chris Angelico" <rosuav@gmail.com> wrote:
On Wed, May 17, 2017 at 12:53 AM, Guido van Rossum <guido@python.org> wrote:
I could also try this myself in my spare time at PyCon
(surprisingly, I
have some!). It sounds kind of interesting. However I've never used the 'attrs' package...
Me neither, so I'm not really an ideal person to head this up. Is there anyone who (a) knows what is and isn't Pythonic, (b) has used 'attrs', and (c) has spare time? It's not an easy trifecta but we can hope!
ChrisA _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
-- --Guido van Rossum (python.org/~guido)
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
-- Stefane Fermigier - http://fermigier.com/ - http://twitter.com/sfermigier - http://linkedin.com/in/sfermigier Founder & CEO, Abilian - Enterprise Social Software - http://www.abilian.com/ Chairman, Free&OSS Group / Systematic Cluster - http://www.gt-logiciel-libre.org/ Co-Chairman, National Council for Free & Open Source Software (CNLL) - http://cnll.fr/ Founder & Organiser, PyData Paris - http://pydata.fr/ --- “You never change things by fighting the existing reality. To change something, build a new model that makes the existing model obsolete.” — R. Buckminster Fuller
On 5/16/17 5:04 PM, Guido van Rossum wrote:
Stephen,
What features of attrs specifically solve your use cases?
Also not Stephan! As others have said, it's the "tupleness" of namedtuple that has bitten me. Also, option mutability is key for my use cases. One use case that attrs satisfies (as does namedtuple) that I'd like to make sure we allow for in any solution is dynamically creating classes where you don't know the field names until runtime. I run in to this when reading anything with columnar metadata, like databases or CSV files. The attr.s "these" parameter solves this for me: fields = ['a', 'b', 'c'] @attr.s(these={f:attr.ib() for f in fields}) class Foo: pass f = Foo(1, 2, 3) print(f) gives: Foo(a=1, b=2, c=3) Mutable default values is a foot-gun. attrs solves it in a similar way to my "namedlist" on PyPI. I'm moving to abandon namedlist and replace it with attrs: the namedlist API is horrible, but a logical (to me!) extension from namedtuple. I mainly wrote it as an exercise in dynamically creating classes using the ast module (as opposed to namedtuple where we build a string definition of the class and exec that). Eric.
On 5/17/17 2:11 AM, Eric V. Smith wrote:
One use case that attrs satisfies (as does namedtuple) that I'd like to make sure we allow for in any solution is dynamically creating classes where you don't know the field names until runtime. I run in to this when reading anything with columnar metadata, like databases or CSV files. The attr.s "these" parameter solves this for me:
fields = ['a', 'b', 'c']
@attr.s(these={f:attr.ib() for f in fields}) class Foo: pass
I should also have mentioned attr.make_class.
On Mon, 15 May 2017 at 08:30 Guido van Rossum <guido@python.org> wrote:
This should be worked into a PEP, instead of living on as a bunch of python-ideas posts and blogs.
I find the attrs documentation (and Glyph's blog post about it) almost unreadable because of the exalted language -- half the doc seems to be *selling* the library more than *explaining* it. If this style were to become common I would find it a disturbing trend.
But having something alongside NamedTuple that helps you declare classes with mutable attributes using the new PEP 526 syntax (and maybe a few variants) would definitely be useful. Will someone please write a PEP? Very few of the specifics of attrs need be retained (its punny naming choices are too much for the stdlib).
In case someone decides to take this on, I wrote a blog post back in March that shows how to use __init_subclass__() to do a rough approximation of what Guido is suggesting: https://snarky.ca/customizing-class-creation-in-python/ . Based on my thinking on the topic while writing my blog post, the tricky bit is going to be deciding how to handle default values (i.e. if you set a default value like `attr: int = 42` on the class definition then you have `cls.attr` exist which might not be what you want if you would rather have the default value explicitly set on every instance but not fall through to the class (e.g. `del ins.attr; ins.attr` raises an AttributeError instead of falling through to `cls.attr`). You could remove the default from the class in your __init_subclass__(), but then you have to decide if that's too unexpected/magical for someone looking at the code. And I too would be interested in seeing something like this, if for any other reason than to help people not to misuse NamedTuple for quick-and-dirty data objects in new APIs (NamedTuple is meant to help move old-style tuple-based APIs to a class-based one). -Brett
--Guido
On Mon, May 15, 2017 at 4:05 AM, Nick Coghlan <ncoghlan@gmail.com> wrote:
Whatever you all propose,
coming from a java and c++ background, OOP in python is quite cumbersome.
if you tell that i am not a python guy, then consider that current oop
On 14 May 2017 at 17:12, Abdur-Rahmaan Janhangeer <arj.python@gmail.com> wrote: style
does not reflect python's style of ease and simplicity
is __init__ really a good syntax choice?
That's a different question, and one with a well-structured third party solution: https://attrs.readthedocs.io/en/stable/
See https://mail.python.org/pipermail/python-ideas/2017-April/045514.html for some ideas on how something like attrs might be adapted to provide better standard library tooling for more concise and readable class definitions.
Cheers, Nick.
-- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
-- --Guido van Rossum (python.org/~guido) _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
On Mon, May 15, 2017 at 9:50 AM, Brett Cannon <brett@python.org> wrote:
On Mon, 15 May 2017 at 08:30 Guido van Rossum <guido@python.org> wrote:
This should be worked into a PEP, instead of living on as a bunch of python-ideas posts and blogs.
I find the attrs documentation (and Glyph's blog post about it) almost unreadable because of the exalted language -- half the doc seems to be *selling* the library more than *explaining* it. If this style were to become common I would find it a disturbing trend.
But having something alongside NamedTuple that helps you declare classes with mutable attributes using the new PEP 526 syntax (and maybe a few variants) would definitely be useful. Will someone please write a PEP? Very few of the specifics of attrs need be retained (its punny naming choices are too much for the stdlib).
In case someone decides to take this on, I wrote a blog post back in March that shows how to use __init_subclass__() to do a rough approximation of what Guido is suggesting: https://snarky.ca/customizing-class-creation-in- python/ .
Based on my thinking on the topic while writing my blog post, the tricky bit is going to be deciding how to handle default values (i.e. if you set a default value like `attr: int = 42` on the class definition then you have `cls.attr` exist which might not be what you want if you would rather have the default value explicitly set on every instance but not fall through to the class (e.g. `del ins.attr; ins.attr` raises an AttributeError instead of falling through to `cls.attr`). You could remove the default from the class in your __init_subclass__(), but then you have to decide if that's too unexpected/magical for someone looking at the code.
I would personally prefer the initializer to stay in the class in cases like this. If the initializer needs to be a default instance of a mutable class (e.g. an empty list or dict) there could be a special marker to indicate that, e.g. attacks: List[int] = MAKE_NEW # Creates a new [] for each instance while if the default needs to be something more custom it could be a similar marker with a callable argument, e.g. fleet: Dict[str, str] = MAKE_NEW(lambda: {'flagship': 'Enterprise'}) I would prefer not to have cleverness like initialization with a callable automatically does something different.
And I too would be interested in seeing something like this, if for any other reason than to help people not to misuse NamedTuple for quick-and-dirty data objects in new APIs (NamedTuple is meant to help move old-style tuple-based APIs to a class-based one).
Not sure I agree that is its only purpose. -- --Guido van Rossum (python.org/~guido)
On Mon, 15 May 2017 at 10:32 Guido van Rossum <guido@python.org> wrote:
On Mon, May 15, 2017 at 9:50 AM, Brett Cannon <brett@python.org> wrote:
On Mon, 15 May 2017 at 08:30 Guido van Rossum <guido@python.org> wrote:
This should be worked into a PEP, instead of living on as a bunch of python-ideas posts and blogs.
I find the attrs documentation (and Glyph's blog post about it) almost unreadable because of the exalted language -- half the doc seems to be *selling* the library more than *explaining* it. If this style were to become common I would find it a disturbing trend.
But having something alongside NamedTuple that helps you declare classes with mutable attributes using the new PEP 526 syntax (and maybe a few variants) would definitely be useful. Will someone please write a PEP? Very few of the specifics of attrs need be retained (its punny naming choices are too much for the stdlib).
In case someone decides to take this on, I wrote a blog post back in March that shows how to use __init_subclass__() to do a rough approximation of what Guido is suggesting: https://snarky.ca/customizing-class-creation-in-python/ .
Based on my thinking on the topic while writing my blog post, the tricky bit is going to be deciding how to handle default values (i.e. if you set a default value like `attr: int = 42` on the class definition then you have `cls.attr` exist which might not be what you want if you would rather have the default value explicitly set on every instance but not fall through to the class (e.g. `del ins.attr; ins.attr` raises an AttributeError instead of falling through to `cls.attr`). You could remove the default from the class in your __init_subclass__(), but then you have to decide if that's too unexpected/magical for someone looking at the code.
I would personally prefer the initializer to stay in the class in cases like this. If the initializer needs to be a default instance of a mutable class (e.g. an empty list or dict) there could be a special marker to indicate that, e.g.
attacks: List[int] = MAKE_NEW # Creates a new [] for each instance
while if the default needs to be something more custom it could be a similar marker with a callable argument, e.g.
fleet: Dict[str, str] = MAKE_NEW(lambda: {'flagship': 'Enterprise'})
I would prefer not to have cleverness like initialization with a callable automatically does something different.
So if I'm understanding your idea correctly: class Foo(DataClass): attr: int = 42 would leave Foo.attr alone, but: class Foo(DataClass): attr: int = MAKE_NEW(42) would be the way to flag that `Foo.attr` shouldn't exist (I'm assuming both options would flag that there should be an `attr` argument to __init__())?
And I too would be interested in seeing something like this, if for any other reason than to help people not to misuse NamedTuple for quick-and-dirty data objects in new APIs (NamedTuple is meant to help move old-style tuple-based APIs to a class-based one).
Not sure I agree that is its only purpose.
My typical thinking on this is I don't want the tuple API that comes with NamedTuple for new APIs, and so that's when I reach for types.SimpleNamespace and have a function that controls the constructor so I can provide a concrete initializer API (e.g. `def foo(a, b): return types.SimpleNamespace(a=a, b=b)`). -Brett
-- --Guido van Rossum (python.org/~guido)
On Sunday, May 14, 2017 at 3:05:46 AM UTC-4, Steven D'Aprano wrote:
On Sun, May 14, 2017 at 04:07:44AM +0000, Simon Ramstedt wrote:
Hi, do you have an opinion on the following?
Hi, and welcome, and of course we have an opinion! This is Python-Ideas, we're very opinionated :-)
Thanks!
Wouldn't it be nice to define classes via a simple constructor function (as below) instead of a conventional class definition?
No.
*conventional*:
class MyClass(ParentClass): def __init__(x): self._x = x def my_method(y): z = self._x + y return z
Looks good to me. It is nicely explicit that you're creating a class, the superclass or superclasses are easy to see, and the attributes are explicit.
*proposed*:
def MyClass(x):
That is the exact same syntax for defining a function called "MyClass", that takes one argument, x. How is Python (and the reader!) supposed to tell which calls to def return a class and which return a function?
self = ParentClass()
What if you have multiple parent classes?
Right, the parent class would have to specifically written to allow
that e.g. via: def ParentClass(obj=None): self = obj or Object() ...
Why is self an instance of the parent class, instead of MyClass?
That's what I've tried to cover under "(+/-) Checking types: ..."
def my_method(y): z = x + y return z
The local variable x is not defined. Wait, is that supposed to come from the closure def MyClass(x)?
What if your class has twenty methods, each of which takes different arguments? Do you have to write:
def MyClass(x, # used in my_method y, # used in another_method z, # used in third_method, a, b, c, # used in fourth_method ... # blah blah blah ):
How does this generalise to non-toy classes, classes with more than one method?
def MyClass would basically as a replacement for __init__. Using __init__ instead for your example would also not be perfect:
def __init__(self, x, y, z, a, b, c): self._x = x self._y = y ... The point is though, that you could still do exactly the same if you wanted to: def MyClass(x, y, z, a, b, c): self = SuperClass() self.x = x self._y = y ... def my_method(): self.x += 5 return self.x + self._y ...
self.my_method = my_method # that's cumbersome (see comments
below)
return self
Here are the pros and cons I could come up with for the proposed method:
(+) Simpler and more explicit.
I think you mean "More complicated and less explicit".
Is this supposed to be some sort of prototype-based OOP instead of class-based OOP? I'd be interested in investigating prototype-based objects, but I don't think this is the way to do it.
-- Steve _______________________________________________ Python-ideas mailing list Python...@python.org <javascript:> https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
Some further thoughts... On Sun, May 14, 2017 at 04:07:44AM +0000, Simon Ramstedt wrote:
*proposed*:
def MyClass(x): self = ParentClass() def my_method(y): z = x + y return z self.my_method = my_method # that's cumbersome (see comments below) return self
I think I misunderstood this earlier. "x" in this case is intended as a parameter to the __init__ method, not as a parameter to the "my_method" method. So my earlier objection doesn't apply: this isn't about the arguments to my_method, it is about the parameters to __init__. To my mind, this is very confusing. The function is called MyClass, leading me to believe that it returns the class object, but it doesn't, it returns a newly instantiated instance. But... given obj = MyClass(1), say, we get: assert type(obj) is ParentClass So MyClass doesn't actually exist anywhere. Worse: def Spam(): self = Parent() # ... def Eggs(): self = Parent() # ... a = Spam() b = Eggs() assert type(a) is type(b) As you point out later in your post:
(-/+) Checking types: In the proposed example above the returned object wouldn't know that it has been created by `MyClass`.
In fact, there is no MyClass class anywhere. That's very strange and confusing.
There are a couple of solutions to that, though. The easiest to implement would be to change the first line to `self = subclass(ParentClass())` where the subclass function looks at the next item in the call stack (i.e. `MyClass`) and makes it the type of the object.
You say this is the easiest to implement. Do you have an implementation? Does it work for CPython, Jython, IronPython, PyPy, Stackless, Nuitka, and other Python implementations? What of Python implementations that don't support intraspection of the call stack? (Perhaps we should just make that a required language feature. But that's a separate argument.)
Another solution would be to have a special rule for functions with capital first letter returning a single object to append itself to the list of types of the returned object. Alternatively there could be a special keyword e.g. `classdef` that would be used instead of `def` if we wouldn't want to rely on the name.
There needs to be some compiler magic happening here. Whether it is in the def keyword or in a new built-in "subclass()" or "classdef", any way you do it it needs to be built into the interpreter. That means its backwards incompatible, and cannot be backported to older versions or alternative implementations. That's not necessarily a fatal objection, but it does mean that the feature needs to add substantial value to the language to make up for the cost of having two ways to do the same thing.
Here are the pros and cons I could come up with for the proposed method:
(+) Simpler and more explicit.
To elaborate on my earlier answer, I really don't think it is simpler. There's a lot of extra compiler magic going on to make it work, and we're lacking a reference to the actual class object itself. You can't say: MyClass.attribute = 999 # add a class attribute because MyClass isn't actually the class, it's a special constructor of the class. Not the __init__ method, or the __new__ method, but a factory function that somehow, behind the scenes, magically creates the class and returns a new instance of it. So to add to the class, you have to write: instance = MyClass() # hope this has no side-effects type(instance).attribute = 999 del instance A couple of other consequences that you might have missed: (1) No docstring: there's no obvious place to declare the class docstring. (2) Every instance gets a new, unique, my_method() object added to it. Every time you call the MyClass constructor/factory function, it creates a brand new my_method object, and attaches it to the instance, self. That's potentially wasteful of memory, but it will work. Well, not quite. As written, it is quite different from the way methods are normally handled. In this new suggested syntax, my_method is actually a function object on the instance, with no self parameter. Since the descriptor protocol is bypassed for objects on self, that doesn't matter, it will work. But now we have two ways of writing methods: - if you write methods using the class keyword, they will be stored in the class __dict__ and they MUST have a `self` parameter; - if you write methods using the constructor function, they will be stored in the instance __dict__, they will be function objects not method objects, they MUST NOT have a `self` parameter, and you cannot use classmethod or staticmethod. I can see this leading to severe confusion when people mistakenly add a self parameter or leave it out. To avoid this, we need yet more magic, or work: instead of writing self.my_method = my_method you have to write something like: type(self).my_method = my_method except that's not right either! That will mean each time you call the constructor function, you replace the my_method for EVERY instance with a new closure. This is not simple at all. [...]
(+) Class/instance level imports would work.
Why do you think that import doesn't work at the class or instance?
(-/+) Speed: The `def`-based objects take 0.6 μs to create while the `class`-based objects take only 0.4 μs. For method execution however the closure takes only 0.15 μs while the proper method takes 0.22 μs (script <https://gist.github.com/rmst/78b2b0f56a3d9ec13b1ec6f3bd50aa9c>).
Premature optimization.
(-) The current syntax for adding a function to an object is cumbersome.
No more cumbersome than any other value: obj.attribue = value works for any value, including functions, provided that it already exists (or can be created using a single expression). If not, then the syntax is no worse for functions than any other multi-statement object: # prepare the value a = [] while condition(): a.append(something()) # attach it to the object obj.attribute = a
That's what is preventing me from actually using the proposed pattern. But is this really the only reason for not using it? And if so, wouldn't that be a good argument for enabling something like below? [...] def self.my_method(y):
That's been proposed at least once before, and probably more than that. I think the most recent time was earlier this year? I don't think I'm against that specific language feature. But nor is it an obviously compelling feature.
or alternatively *multiline lambdas*:
Gosh, what a good idea! I wonder why nobody else has suggested a multi-statement lambda! /sarcasm Multi-statement lambdas have been debated since, oh, Python 1.4 if not earlier. If they were easy to add, we would have had them by now. At the point that your simple proposal to add Javascript-style constructors requires multiple other changes to the language to be practical: - either multi-statement lambda, - or dotted function targets `def self.name`; - a new built-in function subclass() with call stack introspection powers, - or a new keyword classdef, - or significant magic in the def keyword; this is not a simple proposal. And the benefits are marginal, at best: as far as I can see, the best we can hope for is a tiny performance increase by using closures as methods, and some interesting semantic differences: - classmethod and staticmethod won't work (this is bad); - per-instance methods: methods are in the instance __dict__, not the class __dict__ (this might be good, or bad); - bypass the descriptor protocol; - no self (that's a bad thing); - mutator methods need to use the nonlocal keyword (bad). -- Steve
On Sun, May 14, 2017 at 9:14 PM, Steven D'Aprano <steve@pearwood.info> wrote:
There are a couple of solutions to that, though. The easiest to implement would be to change the first line to `self = subclass(ParentClass())` where the subclass function looks at the next item in the call stack (i.e. `MyClass`) and makes it the type of the object.
You say this is the easiest to implement. Do you have an implementation? Does it work for CPython, Jython, IronPython, PyPy, Stackless, Nuitka, and other Python implementations? What of Python implementations that don't support intraspection of the call stack?
(Perhaps we should just make that a required language feature. But that's a separate argument.)
Here's a much easier way to achieve that: @Class def MyClass(...): ... As well as declaring that this is, in fact, a class (more reliable than depending on a naming convention), this decorator could take the return value from the original function and then tag it as a subclass of that object, with the subclass's name being lifted from the function's name. Something like: def Class(func): @wraps(func) def wrapper(*a, **kw): ret = func(*a, **kw) if ret is None: return ret ret.__class__ = func.__name__ # pretend this works return ret return wrapper This would chain, because "self = ParentClass()" will be going through this decorator too. But honestly, I think I'd rather do something like this: def Class(func): class Thing: @wraps(func) def __new__(*a, **kw): func() Thing.__name__ = func.__name__ return Thing In other words, the transformation is to an actual class. The function form becomes syntactic sugar for a particular form of class definition. You could even do subclassing through the decorator: def Class(*parents): def deco(func): class Thing(*parents): # etc AFAIK all this is fully legal in current versions of Python 3. If you want to mess around with classes and functions, decorators can do all that and more, and IMO are the best way to prototype the syntax. No changes needed to grammar or anything. It'll still be a class under the hood, so isinstance checks will work, for instance (sorry, couldn't resist). And it can all be done through a single entry point that you can easily publish in a module on PyPI. ChrisA
participants (25)
-
Abdur-Rahmaan Janhangeer
-
Antoine Rozo
-
Bernardo Sulzbach
-
Boris Borcic
-
Brendan Barnwell
-
Brett Cannon
-
Chris Angelico
-
Eric V. Smith
-
Ethan Furman
-
Gregory P. Smith
-
Guido van Rossum
-
Guido van Rossum
-
Ivan Levkivskyi
-
Juancarlo Añez
-
Michel Desmoulin
-
Nick Coghlan
-
Niki Spahiev
-
Oleg Broytman
-
Simon Ramstedt
-
Stephan Houben
-
Steven D'Aprano
-
Stéfane Fermigier
-
Sven R. Kunze
-
Todd
-
אלעזר