2

Possible Duplicate:
Can I get a reference to the 'owner' class during the init method of a descriptor?

Code is worth a thousand words:

>>> class ShortRib(object):
>>>     def __init__(self, owner):
>>>         self.owner = owner
>>> 
>>>     ... some more methods and stuff ...
>>> 
>>> 
>>> class Cow(object):
>>>     shortRib = ShortRib(self)
>>> 
>>> 
>>> class BrownCow(Cow):
>>>     pass
>>> 
>>> BrownCow.shortRib.owner
<class '__main__.BrownCow'>

This doesn't work, though i wish it would. Basically, I want each class to have some static/class variables (i'm not sure which it is in this case?) but need each of those guys to know who (which class) it belongs to. Unfortunately, I can't "get" at the class in the body of the class declaration. Of course, I could always do this using a decorator:

>>> def vars(**kwargs):
>>>     def wrap(cls):    
>>>         for k, w in kwargs.items():
>>>             setattr(cls, k, w(cls))
>>>         return cls
>>>     return wrap
>>> 
>>> @vars(shortRib=lambda cls: ShortRib(cls)
>>> class BrownCow(Cow):
>>>     ...
>>>
>>> BrownCow.shortRib.owner

which would work. Another way would to have a class decorator that goes through all the shortRibs and similar static variables and sets their owner after the class declaration is complete. However, this seems like an incredibly roundabout and unintuitive way of doing what should be a pretty simple operation: having the static/class members of a class know who they belong to.

Is there a "proper" way of doing this?

Clarification:

I want these members to belong to the class, not to the instances. I'm trying to go for a almost-purely-functional style, using classes only for inheritance of shared behavior, and not creating instances of them at all. Instances would tend to give my functions access to arbitrary instance data shared across all functions, which would break the pure-functioness I am trying for. I could just use empty instances which I don't touch, but I think using pure classes would be cleaner.

Community
  • 1
  • 1
Li Haoyi
  • 15,330
  • 17
  • 80
  • 137
  • Should `owner` be the class of the owner, or the owner object? – tauran Sep 02 '11 at 11:41
  • @Cat - Not a duplicate. Maybe he needs the `ShortRib` bound before an instances are created -- `__init__` might not be early enough. – agf Sep 02 '11 at 11:52
  • Added some clarification. I don't want to use instances at all, I want these things to belong to the class itself. – Li Haoyi Sep 02 '11 at 11:56
  • In that case, your options are acting on the class either while it's being created with a metaclass (as in my answer) or after it's created (like you did with the decorator). Edit -- What you're describing would probably be more cleanly done with free functions in a module instead of instanceless classes -- the functions on the class will still share state if they're class methods just as much as if they were instance methods, if they're static methods, just use free functions in a module. – agf Sep 02 '11 at 11:59
  • @Li Haoyi: Could you elaborate on "Instances would tend to give my functions access to arbitrary instance data shared across all functions"? Classes are instances of `type`. So classes are instances too. Wouldn't any objections you have with instances also apply to classes? – unutbu Sep 02 '11 at 12:06
  • @unutbu: I suppose they would, since in python everything is an instance of something. Mentally, though, I think of objects as "put stuff in here, make many" and classes as "do not touch these, ever". It's like the reason you make objects with the thing.value syntax rather than dicts with the thing['value'] syntax; they are both basically equivalent, but conceptually they are used for different things: dicts are for holding variable keys and objects have fixed keys. Instances and classes are also conceptually different, and though i could use empty instances, i'd rather not if i can help it. – Li Haoyi Sep 02 '11 at 12:55
  • 1
    @Li Haoyi: If the coding is simpler with instances of classes, I'd still go with instances. It's easier to change one's mental paradigms than it is to fight a language's paradigms. – unutbu Sep 02 '11 at 15:14
  • 1
    @unutbu (Li will get autopinnged) He's essentially describing singletons. These are much maligned, but in Python, if you really want to implement them, the best way is either the borg / monostate pattern, or using a metaclass. A search for "Python Singleton" on SO will turn up many examples of how to do this. – agf Sep 02 '11 at 23:24
  • 1
    @unutbu: Just for more context, I am using these singletons to represent web pages, in that each Class represents one page. Since only one page is ever created for one request, and anyway the web scripts should be completely stateless (all the data should be in the DB), it seemed to make sense to use classes purely for inheritance (since many of the pages have huge chunks of view and controller logic in common) and not for instantiation. I still have not decided on the final code structure, so may (or may not) end up switching back to plain instances. Thanks for the help =) – Li Haoyi Sep 03 '11 at 01:53

3 Answers3

2

You can easily do this in __new__:

class ShortRib(object):
    def __init__(self, owner):
        self.owner = owner

class Cow(object):
    shortRib = None
    def __new__(cls, *args, **kwargs):
        if cls.shortRib == None:
            cls.shortRib = ShortRib(cls)
        return super(Cow, cls).__new__(cls, *args, **kwargs)

Cow()
Cow.shortRib.owner

Or even __init__, if you don't mind referencing self.__class___.

You can also do it with a metaclass:

class ShortRib(object):
    def __init__(self, owner):
        self.owner = owner

class MetaCow(type):
    def __new__(cls, name, base, attrs):
        attrs['shortRib'] = ShortRib(cls)
        return super(MetaCow, cls).__new__(cls, name, base, attrs)

class Cow(object):
    __metaclass__ = MetaCow

Cow.shortRib.owner
agf
  • 171,228
  • 44
  • 289
  • 238
1

Why not let the instances of the Cow class have shortRibs, instead of the class itself?:

class ShortRib(object):
    def __init__(self,owner):
        self.owner=owner

class Cow(object):
    def __init__(self):
        self.shortRib=ShortRib(self)

class BrownCow(Cow):
    pass

print(BrownCow().shortRib.owner)
# <__main__.BrownCow object at 0xb76a8d6c>

(Otherwise, you'll need a class decorator or metaclass -- as you've already mentioned. But simple is better than complex, so why not choose simple?)


By the way, if you really do want to use classes instead of instances:

class ShortRib(object):
    def __init__(self, owner):
        self.owner = owner

class MetaCow(type):
    def __init__(cls, name, base, attrs):
        super(MetaCow, cls).__init__(name, base, attrs)
        cls.shortRib = ShortRib(cls)

class Cow(object):
    __metaclass__ = MetaCow

class BrownCow(Cow):
    pass

print(Cow.shortRib.owner)
# <class '__main__.Cow'>

print(BrownCow.shortRib.owner)
# <class '__main__.BrownCow'>

Using

class MetaCow(type):
    def __new__(cls, name, base, attrs):

is incorrect. The signature for type.__new__ is

class MetaCow(type):
    def __new__(meta, name, base, attrs):

Since you want to modify the attributes of cls, not meta, use the MetaCow.__init__ not MetaCow__new__.

unutbu
  • 842,883
  • 184
  • 1,785
  • 1,677
  • This isn't what he asked at all, he obviously already knows how to do this. You can also do it in `__init__` or `__new__` without a decorator or metaclass. – agf Sep 02 '11 at 11:55
  • Please reread the OP's question carefully. – unutbu Sep 02 '11 at 12:21
  • His clarification re-emphasizes he doesn't want to do this (even though it sounds like he's making a design error) – agf Sep 02 '11 at 12:22
  • I'm hoping we can arrive at a better design by encouring the OP to explain why simple does not work. Btw, both your answers also rely on the creation of an instance which is what the OP said he did not want. – unutbu Sep 02 '11 at 12:28
  • no, the metaclass one actually works without creating an instance -- the metaclass' `__new__` is when the class is created, not the instance is created. I just didn't show that. Edited for clarity – agf Sep 02 '11 at 12:30
0

Two methods to to do what you want:

  • You can override the __getattr__ method in any class to return anything you desire when you ask for the value of an attribute.
  • You can use a property, which has a getter that returns the object you want it to return.

Both __getattr__ methods and properties are inherited.

pvoosten
  • 3,247
  • 27
  • 43