It is currently somewhat difficult/awkward to create arbitrary "anonymous" objects in Python. For example, to make some object that simply has a foo() method you have to declare a class that defines foo() and instantiate it: class Foo: def foo(self, x): return x obj = Foo() It would be a bit easier if Python allowed attributes to be set on instances of builtins the way it does for user-defined classes. This would at least allow something like: obj = object() obj.foo = lambda x: x Alas, that is not possible either as the assignment raises an AttributeError when dealing with instances of builtins. I would like to propose a different solution: alter object.__new__() to support keyword arguments and set the attributes named in the keyword arguments to their requested value. This would get us code like: obj = object(foo=1, bar=lambda x: x) obj.foo
1 obj.bar(2) 2
Python implementation of __new__ would look something like the following, though I suppose this would need to be done at the C level in reality: def __new__(typ, **kwargs): obj = object.__new__(typ) for attr, value in kwargs.items(): setattr(obj, attr, value) return obj So why do we need this? Primarily, this sort of thing is very common in tests where it is often useful to generate anonymous objects with default values. As a result, a number of libraries have risen to fill this need which is a pretty strong indicator that it is a useful pattern, and everyone using one of a dozen different libraries to accomplish the same basic task does not seem very Pythonic. I could also see this being quite useful in quick prototyping scenarios in order to avoid using dicts when an object is more suitable without requiring the overhead of defining a class. I'm curious if people on this list would be interested in seeing support for this added to the language, or if there are some good reasons I have overlooked for why this sort of change might be undesirable. Cheers, -Herman
On Sun, 24 Jul 2011 23:04:53 +0900
Herman Sheremetyev
It is currently somewhat difficult/awkward to create arbitrary "anonymous" objects in Python. For example, to make some object that simply has a foo() method you have to declare a class that defines foo() and instantiate it:
class Foo: def foo(self, x): return x obj = Foo()
[...]
obj = object() obj.foo = lambda x: x
Well, really, you're saving *one* line of code all the while making things more obscure (if "obj" gets printed out for whatever reason, you won't ever know it's a Foo or has a foo method).
I would like to propose a different solution: alter object.__new__() to support keyword arguments and set the attributes named in the keyword arguments to their requested value. This would get us code like:
obj = object(foo=1, bar=lambda x: x) obj.foo
1 obj.bar(2) 2
If that's all you need, you just have to write a convenience function that you put in some utilities module: class AnonymousObject: pass def make(**kwargs): obj = AnonymousObject() for k, v in kwargs.items(): setattr(obj, k, v) return obj and then:
from myutils import make obj = make(foo=1, bar=lambda x: x) obj.foo 1 obj.bar(1) 1
Or you can also create a namedtuple class, if there's a well-defined set of attributes your instance(s) will have. Regards Antoine.
Herman Sheremetyev wrote:
It is currently somewhat difficult/awkward to create arbitrary "anonymous" objects in Python. For example, to make some object that simply has a foo() method you have to declare a class that defines foo() and instantiate it: [...]
I'm not sure that "anonymous" is the right word here. Normally when we talk about "anonymous", it is in reference to functions and classes, which have a name given them at creation time. In that sense, early all objects in Python are anonymous: few objects have a __name__ attribute. It seems to me that the name, or lack thereof, of the object is irrelevant to what you propose. Essentially, you want a one-liner for the pattern: class MyObject(object): pass obj = MyObject() obj.spam = "spam spam spam"
So why do we need this? Primarily, this sort of thing is very common in tests where it is often useful to generate anonymous objects with default values. As a result, a number of libraries have risen to fill this need which is a pretty strong indicator that it is a useful pattern, and everyone using one of a dozen different libraries to accomplish the same basic task does not seem very Pythonic. I could also see this being quite useful in quick prototyping scenarios in order to avoid using dicts when an object is more suitable without requiring the overhead of defining a class.
It might be more convincing if you could give examples of those libraries.
I'm curious if people on this list would be interested in seeing support for this added to the language, or if there are some good reasons I have overlooked for why this sort of change might be undesirable.
Against the idea, it's a very simple helper class that you can add to your library or application: class MyObject(object): def __init__(self, **kwargs): self.__dict__.update(kwargs) Three lines, and now you can use it as a one-liner any time you want it: obj = MyObject(flag=True, count=1) In favour of the idea of making it built-in, or at least in the standard library: if this pattern is as common as you say, perhaps there should be a standard solution for it, instead of a plethora of almost, but not quite identical versions. After all, that is one of the main reasons we have bool as a built-in. Also in favour: the implementation given may be slightly *too* simple. It doesn't have a nice repr(). There's no validation of the keyword arguments (although I'm not entirely sure they need validating). The more code needed to make it "professional", the better the argument for doing it once, right, in the standard library. I don't think this needs to be object itself. Doing so probably will cause backwards incompatibility issues -- I'm sure that there will be code out there that expects object(attr="spam") to fail. Better to make a new type. +0.5 on a new type for this, not necessarily a built-in. -1 on changing object to behave this way. -- Steven
On Sun, Jul 24, 2011 at 7:04 AM, Herman Sheremetyev
It is currently somewhat difficult/awkward to create arbitrary "anonymous" objects in Python.
It looks like you are trying to duplicate Java's anonymous classes. Please don't try to emulate Java's work-arounds for Java's deficiencies. -- --Guido van Rossum (python.org/~guido)
On Mon, Jul 25, 2011 at 12:06 AM, Guido van Rossum
On Sun, Jul 24, 2011 at 7:04 AM, Herman Sheremetyev
wrote: It is currently somewhat difficult/awkward to create arbitrary "anonymous" objects in Python.
It looks like you are trying to duplicate Java's anonymous classes. Please don't try to emulate Java's work-arounds for Java's deficiencies.
Ouch, and I don't even know Java well enough to deserve that ;) I think a more accurate characterization would be that I'm trying to emulate Javascript's object literal notation, but I was trying to avoid parallels with other languages in my proposal. FWIW, I think it would be fine if this was a convenience function in the standard library as suggested above. But it seems so basic that having it supported in in the object() constructor would be the most natural place rather than tucking it away in a library. -Herman
On Sun, Jul 24, 2011 at 8:35 AM, Herman Sheremetyev
On Mon, Jul 25, 2011 at 12:06 AM, Guido van Rossum
wrote: On Sun, Jul 24, 2011 at 7:04 AM, Herman Sheremetyev
wrote: It is currently somewhat difficult/awkward to create arbitrary "anonymous" objects in Python.
It looks like you are trying to duplicate Java's anonymous classes. Please don't try to emulate Java's work-arounds for Java's deficiencies.
Ouch, and I don't even know Java well enough to deserve that ;)
Ah, sorry.
I think a more accurate characterization would be that I'm trying to emulate Javascript's object literal notation, but I was trying to avoid parallels with other languages in my proposal.
Still, IMO Javascript's objects suffer from a fatal confusion between being dicts and objects, so I still "object" to the idea. :-)
FWIW, I think it would be fine if this was a convenience function in the standard library as suggested above. But it seems so basic that having it supported in in the object() constructor would be the most natural place rather than tucking it away in a library.
Python's object is supposed to have only the minimal functionality. Its instances don't even have a __dict__. Also, every one believes there own favorite feature to be so important that it's worth changing the language, but they don't want to grant that to anybody else's favorite feature... :-) -- --Guido van Rossum (python.org/~guido)
On 2011-07-24, at 16:30 , Antoine Pitrou wrote:
If that's all you need, you just have to write a convenience function that you put in some utilities module:
class AnonymousObject: pass
def make(**kwargs): obj = AnonymousObject() for k, v in kwargs.items(): setattr(obj, k, v) return obj
and then:
from myutils import make obj = make(foo=1, bar=lambda x: x) obj.foo 1 obj.bar(1) 1 An other option would be to create an anonymous type on the fly to get methods as well (without having to get a hold on the instancemethod constructor):
def make(**kwargs): return type('', (), kwargs)()
obj = make(foo=1, bar=lambda self, x: x) obj.foo 1 obj.bar(2) 2
although it makes attributes provided via kwargs into class attributes (which probably does not matter since the class is unique to the object)
On Mon, Jul 25, 2011 at 2:03 AM, Guido van Rossum
Also, every one believes there own favorite feature to be so important that it's worth changing the language, but they don't want to grant that to anybody else's favorite feature... :-)
We've actually been down the 'namespace object' road years ago (Steve Bethard even wrote a proto-PEP IIRC) and it suffered the same fate as most enum PEPs: everyone has slightly different ideas on what should be supported, so you end up being faced with one of two options: 1. Ignore some of the use cases (so some users still have to do their own thing) 2. Support all of the use cases by increasing the API complexity (so many users will still do their own thing instead of learning the new API) In this space, collections.namedtuple is a well-executed variant of the first alternative. There are some use cases it doesn't handle, but it covers many of them and does it well. For the OP's use case, I'll simply note that this functionality is already provided by the type builtin: obj = type('Foo', (), dict(foo=(lambda x: x)))() Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia
You could use a class decorator to help with this. def instance(cls): return cls() @instance class foo(object): x = 42 -- Greg
@apply
Or is that too evil?
Devin
On Sun, Jul 24, 2011 at 7:48 PM, Greg Ewing
You could use a class decorator to help with this.
def instance(cls): return cls()
@instance class foo(object): x = 42
-- Greg _______________________________________________ Python-ideas mailing list Python-ideas@python.org http://mail.python.org/mailman/listinfo/python-ideas
On Sun, Jul 24, 2011 at 7:48 PM, Greg Ewing
wrote: You could use a class decorator to help with this.
def instance(cls): return cls()
@instance class foo(object): x = 42
On Sun, Jul 24, 2011 at 5:14 PM, Devin Jeanpierre
@apply
Or is that too evil?
Apparently so much so that Python 3 eliminated it. Cheers, Chris
On Mon, Jul 25, 2011 at 1:03 AM, Guido van Rossum
On Sun, Jul 24, 2011 at 8:35 AM, Herman Sheremetyev
wrote: On Mon, Jul 25, 2011 at 12:06 AM, Guido van Rossum
wrote: On Sun, Jul 24, 2011 at 7:04 AM, Herman Sheremetyev
wrote: It is currently somewhat difficult/awkward to create arbitrary "anonymous" objects in Python.
It looks like you are trying to duplicate Java's anonymous classes. Please don't try to emulate Java's work-arounds for Java's deficiencies.
Ouch, and I don't even know Java well enough to deserve that ;)
Ah, sorry.
I think a more accurate characterization would be that I'm trying to emulate Javascript's object literal notation, but I was trying to avoid parallels with other languages in my proposal.
Still, IMO Javascript's objects suffer from a fatal confusion between being dicts and objects, so I still "object" to the idea. :-)
FWIW, I think it would be fine if this was a convenience function in the standard library as suggested above. But it seems so basic that having it supported in in the object() constructor would be the most natural place rather than tucking it away in a library.
Python's object is supposed to have only the minimal functionality. Its instances don't even have a __dict__.
What if there was a base "dummy" class that would allow this kind of behavior as Steven suggested? My choice of object was simply because it seems like the logical choice among the builtins and to get a discussion going about the idea of creating objects as an expression rather than statement, without resorting to defining a new class just for that purpose. For a short list of libraries that do this sort of thing (not all of them support expression syntax) take a look at the "Simple fake object" section in: http://garybernhardt.github.com/python-mock-comparison/ Disclaimer: I'm the author of the flexmock library listed there. There are also probably another half dozen libraries no longer being maintained that are not listed there but have similar functionality that can be found here: http://pycheesecake.org/wiki/PythonTestingToolsTaxonomy#MockTestingTools As well as a couple of new ones, like Chai, that aren't listed in the testing taxonomy but solve the same problem. Cheers, -Herman
On Mon, Jul 25, 2011 at 7:47 AM, Nick Coghlan
On Mon, Jul 25, 2011 at 2:03 AM, Guido van Rossum
wrote: Also, every one believes there own favorite feature to be so important that it's worth changing the language, but they don't want to grant that to anybody else's favorite feature... :-)
We've actually been down the 'namespace object' road years ago (Steve Bethard even wrote a proto-PEP IIRC) and it suffered the same fate as most enum PEPs: everyone has slightly different ideas on what should be supported, so you end up being faced with one of two options: 1. Ignore some of the use cases (so some users still have to do their own thing) 2. Support all of the use cases by increasing the API complexity (so many users will still do their own thing instead of learning the new API)
In this space, collections.namedtuple is a well-executed variant of the first alternative. There are some use cases it doesn't handle, but it covers many of them and does it well.
For the OP's use case, I'll simply note that this functionality is already provided by the type builtin:
obj = type('Foo', (), dict(foo=(lambda x: x)))()
This one-liner with type is neat. For the record, it's missing "self" so a working version would be a tad bit longer. Using an empty name and using {} instead of dict() makes it look almost usable: obj = type('', (), {'foo': lambda self, x: x})() I think it would actually *be* usable if the dictionary passed in as the last argument could also be built out of keyword arguments: obj = type('', (), foo=lambda self, x: x)() Giving those first two positional arguments default values (empty string and empty tuple?) would make it even better: obj = type(foo=lambda self, x: x)() That's only one set of parens away from my initial proposal, but I think it's close enough. Would there be any objections to changing the type() API to support this? Cheers, -Herman
On 07/25/2011 05:44 AM, Herman Sheremetyev wrote:
Giving those first two positional arguments default values (empty string and empty tuple?) would make it even better:
obj = type(foo=lambda self, x: x)()
That's only one set of parens away from my initial proposal, but I think it's close enough. Would there be any objections to changing the type() API to support this?
-1 on changing type(). Just add this to your code:
def mytype(**kwargs): ... return type('', (), kwargs)()
Then you can get rid of the extra parens:
obj=mytype(foo=lambda self, x: x) obj.foo(3) 3
Eric.
Am 25.07.2011 16:38, schrieb Eric V. Smith:
On 07/25/2011 05:44 AM, Herman Sheremetyev wrote:
Giving those first two positional arguments default values (empty string and empty tuple?) would make it even better:
obj = type(foo=lambda self, x: x)()
That's only one set of parens away from my initial proposal, but I think it's close enough. Would there be any objections to changing the type() API to support this?
-1 on changing type(). Just add this to your code:
def mytype(**kwargs): ... return type('', (), kwargs)()
Then you can get rid of the extra parens:
obj=mytype(foo=lambda self, x: x) obj.foo(3) 3
And if you call it anonymous_object() instead of mytype(), you have the advantage of more readable code as well ;) Georg
On Jul 24, 2011, at 09:03 AM, Guido van Rossum wrote:
Also, every one believes there own favorite feature to be so important that it's worth changing the language, but they don't want to grant that to anybody else's favorite feature... :-)
This reminds me of the poll you conducted at a keynote, many Pycons ago. You first asked "who thinks the language is changing too quickly?" Lots of hands went up. Then you asked (probably paraphrasing) "who has a feature they think needs to go into Python?" Lots of the same hands went up. :) -Barry
On 07/25/2011 12:51 PM, Georg Brandl wrote:
Am 25.07.2011 16:38, schrieb Eric V. Smith:
On 07/25/2011 05:44 AM, Herman Sheremetyev wrote:
Giving those first two positional arguments default values (empty string and empty tuple?) would make it even better:
obj = type(foo=lambda self, x: x)()
That's only one set of parens away from my initial proposal, but I think it's close enough. Would there be any objections to changing the type() API to support this?
-1 on changing type(). Just add this to your code:
def mytype(**kwargs): ... return type('', (), kwargs)()
Then you can get rid of the extra parens:
obj=mytype(foo=lambda self, x: x) obj.foo(3) 3
And if you call it anonymous_object() instead of mytype(), you have the advantage of more readable code as well ;)
Yes, yes :). In my defense, moving the call into "mytype" was a last minute change. Eric.
On Tue, Jul 26, 2011 at 2:52 AM, Barry Warsaw
On Jul 24, 2011, at 09:03 AM, Guido van Rossum wrote:
Also, every one believes there own favorite feature to be so important that it's worth changing the language, but they don't want to grant that to anybody else's favorite feature... :-)
This reminds me of the poll you conducted at a keynote, many Pycons ago. You first asked "who thinks the language is changing too quickly?" Lots of hands went up. Then you asked (probably paraphrasing) "who has a feature they think needs to go into Python?" Lots of the same hands went up. :)
Heh, I used the exact same point as my first section heading when blogging about some of the reasons I think python-dev is the way it is: "Python evolves too slowly! You're changing the language too fast!" (http://www.boredomandlaziness.org/2011/04/musings-on-culture-of-python-dev.h...). Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia
On Thu, Aug 4, 2011 at 2:17 AM, dag.odenhall@gmail.com
obj = object(foo=1, bar=lambda x: x) obj.foo
1 obj.bar(2) 2
Problem? :)
from argparse import Namespace obj = Namespace(foo=1, bar=lambda x: x) obj.foo 1 obj.bar(2) 2
Good find, who'd have ever thought to look in argparse for something like Namespace.. After some more poking around in the stdlib for __init__ methods that take keyword args I found: plistlib.Dict and plistlib.Plist Both of them do the same thing as well as give access to the values using [] notation. There are probably other examples in third party libraries. -Herman
On Wed, Aug 3, 2011 at 11:49 PM, Herman Sheremetyev
Both of them do the same thing as well as give access to the values using [] notation. There are probably other examples in third party libraries.
Sure. Another stdlib-ish example is Sphinx, which does it in
sphinx.util.attrdict. And you can search google code for
'self.__dict__ = self', which reveals a particular(ly nasty) pattern
for it with many instances.
Devin
On Wed, Aug 3, 2011 at 11:49 PM, Herman Sheremetyev
On Thu, Aug 4, 2011 at 2:17 AM, dag.odenhall@gmail.com
wrote: obj = object(foo=1, bar=lambda x: x) obj.foo
1 obj.bar(2) 2
Problem? :)
from argparse import Namespace obj = Namespace(foo=1, bar=lambda x: x) obj.foo 1 obj.bar(2) 2
Good find, who'd have ever thought to look in argparse for something like Namespace.. After some more poking around in the stdlib for __init__ methods that take keyword args I found:
plistlib.Dict and plistlib.Plist
Both of them do the same thing as well as give access to the values using [] notation. There are probably other examples in third party libraries.
-Herman _______________________________________________ Python-ideas mailing list Python-ideas@python.org http://mail.python.org/mailman/listinfo/python-ideas
On 4 August 2011 06:39, Devin Jeanpierre
On Wed, Aug 3, 2011 at 11:49 PM, Herman Sheremetyev
wrote: Both of them do the same thing as well as give access to the values using [] notation. There are probably other examples in third party libraries.
Sure. Another stdlib-ish example is Sphinx, which does it in sphinx.util.attrdict. And you can search google code for 'self.__dict__ = self', which reveals a particular(ly nasty) pattern for it with many instances.
Devin
I heard that particular pattern leaks memory?
I heard that particular pattern leaks memory?
I don't believe it does anymore, because of the cyclic garbage collector.
Devin
On Thu, Aug 4, 2011 at 9:54 AM, dag.odenhall@gmail.com
On 4 August 2011 06:39, Devin Jeanpierre
wrote: On Wed, Aug 3, 2011 at 11:49 PM, Herman Sheremetyev
wrote: Both of them do the same thing as well as give access to the values using [] notation. There are probably other examples in third party libraries.
Sure. Another stdlib-ish example is Sphinx, which does it in sphinx.util.attrdict. And you can search google code for 'self.__dict__ = self', which reveals a particular(ly nasty) pattern for it with many instances.
Devin
I heard that particular pattern leaks memory?
participants (14)
-
Antoine Pitrou
-
Barry Warsaw
-
Chris Rebert
-
dag.odenhall@gmail.com
-
Devin Jeanpierre
-
Eric V. Smith
-
Georg Brandl
-
Greg Ewing
-
Guido van Rossum
-
Herman Sheremetyev
-
Masklinn
-
Nick Coghlan
-
Raymond Hettinger
-
Steven D'Aprano