Does Python have anonymous classes?
Asked Answered
M

9

95

I'm wondering if Python has anything like the C# anonymous classes feature. To clarify, here's a sample C# snippet:

var foo = new { x = 1, y = 2 };
var bar = new { y = 2, x = 1 };
foo.Equals(bar); // "true"

In Python, I would imagine something like this:

foo = record(x = 1, y = 2)
bar = record(y = 2, x = 1)
foo == bar  # true

The specific requirement is being able to create an object with specified fields in expression context (e.g. usable in lambdas and other places where statements aren't allowed), with no additional external declarations, and ability to access individual components by name via the normal member access syntax foo.bar. The created object should also implement structural comparison by component names (not by position, as tuples do).

In particular: tuples isn't it because their components are not named; classes isn't it because they require a declaration; dicts isn't it because they have undesired foo["bar"] syntax to access components.

namedtuple isn't it, because it still requires a name even if you define the type inline, and the comparison is position-based, not name-based. In particular:

 def foo(): return namedtuple("Foo", "x y")(x = 1, y = 2)
 def bar(): return namedtuple("Foo", "y x")(x = 1, y = 2)
 foo() == bar()   # False because fields are compared in order, and not by name
                  # True would be desired instead

I know how to write such a thing in Python if needed. But I would like to know if there's anything like that in the Python standard library, or any popular third-party libraries.

[EDIT]

Just for the sake of it, here's a single-expression solution that combines two very informative answers by Ken and alanlcode, yielding structural equality without any extra outside declarations:

type("", (), { \
    "__init__": (lambda self, **kwargs: self.__dict__.update(kwargs)), \
    "__eq__": (lambda self, other: self.__dict__ == other.__dict__) } \
)(x = 1, y = 2)

Technically, it satisfies all the requirements of the question, but I sincerely hope that no-one ever uses it (I definitely won't).

Marindamarinduque answered 14/7, 2009 at 1:22 Comment(7)
sounds like dictionaries should do the job. I find it's best to find the python way to do it than fit another language in to python. BTW -- if you don't like the dictionary, foo["bar"] access method an alternative is to use the get method: foo.get("bar")Nelan
Given that this is trivially implementable in Python if needed, I don't see any particular reason not to do so, and definitely don't consider it "fitting another language in to Python". Especially as it seems pretty close to the existing namedtuple in intent.Marindamarinduque
I find it bizarre to ask a question if language X has the feature of language Y, and then require everything to be exactly the same. Languages are not exactly the same. Python does not have anonymous functions, but they have dictionaries, and they work just as well. Yes, the access syntax is different. Big friggin deal.Speedball
I'm not asking for feature to be exactly the same - if I did, I'd be asking for static typing and immutability as well ;) I'm merely asking for syntax which I view as more natural and convenient.Marindamarinduque
I to had a tendency to create objects so I could stick data on them for attribute access in the beginning. You'll get used to dicts soon.Speedball
See also: https://mcmap.net/q/37862/-create-an-anonymous-class-instance-in-python/680372Phanerogam
You don't need those backslashes. In Python, a line hasn't ended until the brackets have all closed.Rickie
C
56

The pythonic way would be to use a dict:

>>> foo = dict(x=1, y=2)
>>> bar = dict(y=2, x=1)
>>> foo == bar
True

Meets all your requirements except that you still have to do foo['x'] instead of foo.x.

If that's a problem, you could easily define a class such as:

class Bunch(object):
    def __init__(self, **kwds):
        self.__dict__.update(kwds)

    def __eq__(self, other):
        return self.__dict__ == other.__dict__

Or, a nice and short one

class Bunch(dict):
    __getattr__, __setattr__ = dict.get, dict.__setitem__

(but note that this second one has problems as Alex points out in his comment!)

Cultivation answered 14/7, 2009 at 1:32 Comment(7)
From the question - " ... dicts isn't it because they have undesired foo["bar"] syntax to access components."Amongst
this works, except to access things it is foo['x'], not foo.xBedell
as I mentioned in my main comment, dictionaries also have a 'get' method for retireving a key's value: foo.get('bar')Nelan
Nonetheless, the request is what it is, and this doesn't answer it. I also fail to see how it is "un-Pythonic", considering the existence of namedtuple, which gives most of the syntactic sugar that I want.Marindamarinduque
I think "Bunch" (first form) is the right answer (but I'm biased since I coined the name, 8+ years ago -- see code.activestate.com/recipes/52308 which btw is the top google hit for the search [bunch python];-). The second form has deep and somewhat subtle problems, do x=Bunch(update=23) and see what x.update IS;-) -- you call that NICE?-)Athanasius
@Alex: How about setting __getattribute__ = dict.get? Ugly, yes, but does it still have problems?Cultivation
Well, it seems that there isn't any good answer that satisfies all my criteria, and this one is the most extensive otherwise, covering both the "you don't really want to" angle and the "but anyway here's how" angle :) - so I'm accepting it.Marindamarinduque
M
82

Looks like Python 3.3 has added exactly this thing in the form of types.SimpleNamespace class.

Marindamarinduque answered 11/12, 2012 at 21:29 Comment(2)
There are so many hidden bits of python that really ought to be more prominent and embedded in the language like this. There seems to be some reluctance to actually modernise the language itself rather than adding these bolt-ons. The answer worked for me, I'll just need to keep coming back to this every time I want do to it!Ethics
Is there an anonymous type-hint to go with SimpleNamespace? Say i am defining a function that returns SimpleNamespace(x= ..., y= ...), I want to be able to type-hint the result, in a manner like: def func() -> SimpleNamespace(x: int, y: str) (the syntax will have to change, though). This is similar to what one can do in the case of a tuple as the return type (namely, def func() -> Tuple[int, str]).Woodpecker
C
56

The pythonic way would be to use a dict:

>>> foo = dict(x=1, y=2)
>>> bar = dict(y=2, x=1)
>>> foo == bar
True

Meets all your requirements except that you still have to do foo['x'] instead of foo.x.

If that's a problem, you could easily define a class such as:

class Bunch(object):
    def __init__(self, **kwds):
        self.__dict__.update(kwds)

    def __eq__(self, other):
        return self.__dict__ == other.__dict__

Or, a nice and short one

class Bunch(dict):
    __getattr__, __setattr__ = dict.get, dict.__setitem__

(but note that this second one has problems as Alex points out in his comment!)

Cultivation answered 14/7, 2009 at 1:32 Comment(7)
From the question - " ... dicts isn't it because they have undesired foo["bar"] syntax to access components."Amongst
this works, except to access things it is foo['x'], not foo.xBedell
as I mentioned in my main comment, dictionaries also have a 'get' method for retireving a key's value: foo.get('bar')Nelan
Nonetheless, the request is what it is, and this doesn't answer it. I also fail to see how it is "un-Pythonic", considering the existence of namedtuple, which gives most of the syntactic sugar that I want.Marindamarinduque
I think "Bunch" (first form) is the right answer (but I'm biased since I coined the name, 8+ years ago -- see code.activestate.com/recipes/52308 which btw is the top google hit for the search [bunch python];-). The second form has deep and somewhat subtle problems, do x=Bunch(update=23) and see what x.update IS;-) -- you call that NICE?-)Athanasius
@Alex: How about setting __getattribute__ = dict.get? Ugly, yes, but does it still have problems?Cultivation
Well, it seems that there isn't any good answer that satisfies all my criteria, and this one is the most extensive otherwise, covering both the "you don't really want to" angle and the "but anyway here's how" angle :) - so I'm accepting it.Marindamarinduque
M
54

1) See http://uszla.me.uk/space/blog/2008/11/06. You can create an anonymous object with slightly ugly syntax by using the type built-in function:

 anon_object_2 = type("", (), {})()

where the 3rd parameter is the dict that will contain the fields of your object.

 foo = type("", (), dict(y=1))()
 foo.y == 1

2) Another variation is proposed by Peter Norvig at http://norvig.com/python-iaq.html. It is also similar to the answer posted by Ken.

class Struct:
    def __init__(self, **entries): self.__dict__.update(entries)

>>> options = Struct(answer=42, linelen = 80, font='courier')
>>> options.answer
42

The benefit of this method is that you can implement equality by contents of the dict, which the first option doesn't have.

Mandelbaum answered 14/7, 2009 at 1:40 Comment(2)
I like the 'struct' class. Very useful to me, as my problem didn't require any special methods or operators. In a nutshell, I am using suds/soap and most of the time suds will build a 'reply' object for me, who's structure defined by a WSDL. If you get bad XML, the sax parser throws an exception leaving you with no reply object. I 'fake' a reply object with the Struct class above (setting only properties 'error' and 'message' per my application) and pass this downstream. If error handling ever expects more properties (or methods?) It's easy to add (extend?) this class to match. WIN.Silk
anon_object_2 = type("", (), {})() <-- this is BRILLIANTAerospace
P
6

I don't remember offhand if there's a built-in but writing it yourself is shorter than typing your question. :-)

class record(object):
  def __init__(self, **kwargs): self.__dict__ = kwargs
  def __eq__(self, r2): return self.__dict__ == r2.__dict__
  def __ne__(self, r2): return self.__dict__ != r2.__dict__

foo = record(x=1, y=2)
bar = record(y=2, x=1)
foo == bar  # => true
Pillsbury answered 14/7, 2009 at 1:36 Comment(4)
Neat (I knew how to do this in general, but didn't realize it's that simple). Now, on to the real question: would you be willing to submit a PEP for the above? :)Marindamarinduque
By the way, is __neq__ really needed? Isn't the default definition as not __eq__ provided automatically?Marindamarinduque
Pavel: that's what I initially thought, but when I tried it, it didn't seem to work that way (though it's quite possible I screwed up).Pillsbury
__neq__ should be __ne__, and no it is not provided automatically.Waldman
C
6

The type(...) form will fail the structural comparison requirement (without getting really ugly). The dict(...) form doesn't meet the attribute accessor requirement.

The attrdict seems to fall in the middle somewhere:

class attrdict(dict):
    def __init__(self, *args, **kwargs):
        dict.__init__(self, *args, **kwargs)
        self.__dict__ = self

a = attrdict(x=1, y=2)
b = attrdict(y=2, x=1)

print a.x, a.y
print b.x, b.y
print a == b

But it means defining a special class.

OK, I just noticed the update to the question. I'll just note that you can specify dict for the bases parameter and only need to specify the constructor then (in the icky type expression). I prefer attrdict. :-)

Corrosive answered 14/7, 2009 at 2:7 Comment(2)
Setting self.__dict__ = self causes a memory leak, so I'd advise against this. bugs.python.org/issue1469629Glossography
Isn't the __init__ call supposed to look like this super(attrdict, self).__init__(*args, **kwargs)?Siva
X
5

If you want the instance to be anonymous as well (using the object directly in an expression), then you're bound to use the type-expression. However in many cases the instance will not be anonymous, but assigned to a variable. This case can be handled in a reasonable way in python by using metaclasses or decorators.

An example using decorator:

def anonymous(cls):
    return cls()

@anonymous
class foo:
     x = 42

     def bar(self):
          return self.x

The decorator in this case causes the class foo to be instantiated an put into the variable foo instead of the class itself. The class itself will not be accessible from any namespace although it has a name:

>>> foo
<__main__.foo instance at 0x7fd2938d8320>
>>> foo.bar()
42

Another feature in python that accomodates for many use cases is that it's legal to define classes locally, which means that they would become a symbol local to that function, which in turns gives it some degree of anonymity.

Xhosa answered 30/5, 2015 at 6:28 Comment(0)
D
4

Quoted from this page:

 class Struct:
     def __init__(self, **entries): self.__dict__.update(entries)
     def __eq__(self, other): return self.__dict__ == other.__dict__
     def __ne__(self, other): return self.__dict__ != other.__dict__

 options = Struct(answer=42, linelen = 80, font='courier')
 options.answer
 >>> 42
 options.answer = 'plastics'
 vars(options)
 >>> {'answer': 'plastics', 'font': 'courier', 'linelen': 80}
Dittmer answered 14/7, 2009 at 1:40 Comment(1)
it's not neq it's neStolen
M
0

Expanding on skyking's response, I came up with an equivalent from anonymous classes in java (which was not asked, but what I was looking for).

I define the following decorator:

def newinstance(*args, **kwargs):
    def decorator(cls):
        return cls(*args, **kwargs)
    return decorator

The following java code

MyClass obj = new MyClass(arg) {
    public void method() {
        // body of the method
    }
};

is roughly equivalent to:

@newinstance(arg)
class obj(MyClass):
    def method(self):
        pass # body of the method
Madelenemadelin answered 4/1, 2023 at 3:33 Comment(0)
C
-2

enter image description here

As show above, I get a good intellisense from pycharm for this and I love this solution...

>>> def anonymous(cls):
...     return cls()
>>> class fooo:
...     @anonymous
...     class c:
...             D=10
>>> fooo.c.D
10
Coom answered 2/8, 2018 at 7:27 Comment(1)
@Xhosa using you hint and I love this... I which I had more provisions on stackoverflowCoom

© 2022 - 2024 — McMap. All rights reserved.