tags:

views:

230

answers:

6

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

+3  A: 

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__
''
gnibbler
this is nice and hacky :)
Stefano Borini
Sorry gnibbler, you answer is good, but Alex' one left me amazed :)
Stefano Borini
did noone see the joke here? it's the same as `class status(object): pass`.
Matt Joiner
+4  A: 

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.

Arrieta
Ugh, this is old documentation and they should clean it up.This example code creates an "old-style class". This is not recommended practice anymore, and in Python 3.x it will not even work.To do this exact example as a new-style class that will work in Python 3.x:`class Employee(object): pass`That's all you have to do, just inherit from `object` and you have a new-style class.
steveha
This is exactly like the Anon example the OP said they don't want to do.
gnibbler
Yes, that's good. It was more annoyed by the fact you have to define class Status: pass and then status = Status().
Stefano Borini
@gnibbler: but it works better than any alternative presented so far. I just pointed out that in the language design, it was considered that this was the best way to make "struct-like" instances.
Arrieta
@steveha: what is an "old-style class"?
Arrieta
@steveha: At least with Python 3.1.1, the example still works.
nd
@nd and @steveha You shouldn't need to explicitly inherit from object in Python 3. In Python 3, inheriting from object is the default. There are no old-style classes.
AFoglia
@Arrieta A few versions ago, Python introduced a new class implementation. It does everything the old one did, and more (such as allowing derivation from the standard list and dictionary types, and features such as a real constructor and properties). For backwards compatibility, you had to explicitly derive from `object`. Since Python 3 is not backwards compatible, the derivation is unnecessary.
AFoglia
+1  A: 

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.

darkporter
`object()` does return an instance. It's used sometimes if you need a unique value
gnibbler
+2  A: 

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.

steveha
+8  A: 

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()...;-).

Alex Martelli
Only if you don't count the characters explaining what the heck it's for! :D
gnibbler
woah! :D +1000 for Alex
Stefano Borini
@Stefano, thanks! @gnibbler, lambda's sufficiently mystical that you don't have to explain, you just make mysterious finger gestures in the air and everybody backs off in fear (optionally, darkly murmur "lambda, the Ultimate...!", http://lambda-the-ultimate.org/, and/or see http://en.wikipedia.org/wiki/Lambda for many other obscure references).
Alex Martelli
Uh-oh ... the martellibot has been subverted and gone over to the dark side :-(
John Machin
At first, this boggled my mind. But it's actually simple. The `lambda` creates a function object; this one happens to be a trivial function that returns 0. Function objects have certain attributes, among them a `func_dict` member. This works because it works for any function object; you can bind values to names associated with any function object. I have used that to provide "enumerated" flags that can be used when calling the function: `foo(0, foo.ALTERNATE_MODE)` This made-up example shows calling a function with an optional flag that requests some sort of alternate mode.
steveha
@steveha: +1 - Named constants for function call: excellent use for function attributes.
Don O'Donnell
+3  A: 

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 (no reputation, so only one link for me.) Not that the PEPs directly relate to your question, but I think it's useful in starting to use Python in a Pythonic manner.

Mark Evans