struct objects in python
I wanted to create a throwaway "struct" object to keep various status flags. My first approach was this (javascript style)
>>> status = object()
>>> status.foo = 3
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'object' object 开发者_StackOverflow社区has no attribute 'foo'
Definitely not what I expected, because this works:
>>> class Anon: pass
...
>>> b=Anon()
>>> b.foo = 4
I guess this is because object() does not have a __dict__
. I don't want to use a dictionary, and assuming I don't want to create the Anon object, is there another solution ?
The most concise way to make "a generic object to which you can assign/fetch attributes" is probably:
b = lambda:0
As most other answers point out, there are many other ways, but it's hard to beat this one for conciseness (lambda:0
is exactly the same number of characters as object()
...;-).
From the Python Official Documentation:
9.7. Odds and Ends
Sometimes it is useful to have a data type similar to the Pascal “record” or C “struct”, bundling together a few named data items. An empty class definition will do nicely:
class Employee:
pass
john = Employee() # Create an empty employee record
# Fill the fields of the record
john.name = 'John Doe'
john.dept = 'computer lab'
john.salary = 1000
This seems natural and simple: Pythonic. Remember the Zen! "Simple is better than complex" (number 3) and "If the implementation is easy to explain, it may be a good idea" (number 11)
In addition, a struct
is nothing but a class
with public members (i.e., struct{};
and class{public:};
are the exact same thing (in, say, C++)). Shouldn't you consider this and avoid artificial constructs in your Python program? Python is supposed to be readable, maintainable, and easy to understand.
I had the same question once. I asked it in a mailing list, and Alex Martelli pointed out that object
is the basis of all inheritance in Python; if object()
created a class instance with its own dictionary, then every object in Python would have to have its own dictionary, and that would waste memory. For example, True
and False
are objects; clearly they don't have any need for their own dictionaries!
I would be happy if there was some sort of built-in Python feature where I could just say:
x = struct()
x.foo = 1
x.bar = 2
But it is trivial to write struct()
:
class struct(object):
pass
Or you could do a slightly more complex one:
class struct(object):
def __init__(self, **kwargs):
self.__dict__.update(kwargs)
The more complex one lets you do this:
x = struct(foo=1, bar=2)
print(x.foo) # prints 1
print(x.bar) # prints 2
x.baz = 3
print(x.baz) # prints 3
But it is so trivial to write struct()
that I guess it wasn't deemed worth adding to the language. Maybe we should push to have a standard feature added to collections
module or something.
I personally think that the cleanest solution is what you already guessed:
class Scratch(object):
pass
s = Scratch()
s.whatever = 'you want'
I know you said that you don't want a __dict__
, but that confuses me as I can't see a reason to care about that. You don't have to reference __dict__
, that is an internal Python implementation detail. Anyway, any instance in Python that allows dynamically adding attributes will have a __dict__
because that's how Python does dynamic attributes. Even if the instance is created in a really clever way, it will have a __dict__
.
If you have not already done so, I recommend reading PEP 20 and PEP 8. Not that the PEPs directly relate to your question, but I think it's useful in starting to use Python in a Pythonic manner.
Try this
>>> status=type('status',(object,),{})()
>>> status.foo=3
>>> status.foo
3
You don't have to give the class a name if you don't want to
>>> status=type('',(object,),{})()
>>> status.__class__.__name__
''
Of course there's always a namedtuple
. It's cheap and fast, but immutable -- you must know your attribute names and values in advance, and you can't alter their values later. But at least it has the struct/object attribute getters. And it'll work with Python 2 and 3
>>> from collections import namedtuple
>>> Status = namedtuple('Status', 'a b')
>>> s = Status(a=1, b=2)
>>> s.a + s.b
3
The mystery here is the difference between objects and class instances.
In Python, everything is an object. Classes are objects, integers are objects, types are objects, and class instances are objects. When you say object()
you're getting a plain base-level object. It's nothing. Completely useless. Lower level than anything else you can reference in Python.
You probably thought calling object()
gives you a class instance. Which is understandable, because you probably thought object
is a class. It's not. Even though you might think so since it's the base "class" used for new-style class definitions like:
class MyClass(object):
pass
object
is in fact a type (like how str
and int
are types). When you call object()
you're not constructing a class instance, your instantiating a special type of object. But in object
's case, it's special in how completely blah it is.
Only class instances have the special ability to tack things on with dot notation. That's not a general property of all objects. Imagine if that were the case! You could do crazy stuff like adding properties to strings:
s = "cat"
s.language = "english"
Obviously you can't do that.
If you want the semantics of a Javascript object, you could consider using the namespaces library. You just need to pip install namespaces
first.
>>> import namespaces as ns
>>> b = ns.Namespace()
>>> b.foo = 4
>>> b
Namespace(foo=4)
full disclaimer: I am the author of the namespaces
library.
精彩评论