__getattr__ for static/class variables in python

前端 未结 5 1349
臣服心动
臣服心动 2020-11-28 07:20

I have a class like:

class MyClass:
     Foo = 1
     Bar = 2

Whenever MyClass.Foo or MyClass.Bar is invoked, I n

相关标签:
5条回答
  • 2020-11-28 07:46

    __getattr__() and __str__() for an object are found on its class, so if you want to customize those things for a class, you need the class-of-a-class. A metaclass.

    class FooType(type):
        def _foo_func(cls):
            return 'foo!'
    
        def _bar_func(cls):
            return 'bar!'
    
        def __getattr__(cls, key):
            if key == 'Foo':
                return cls._foo_func()
            elif key == 'Bar':
                return cls._bar_func()
            raise AttributeError(key)
    
        def __str__(cls):
            return 'custom str for %s' % (cls.__name__,)
    
    class MyClass:
        __metaclass__ = FooType
    
    # in python 3:
    # class MyClass(metaclass=FooType):
    #    pass
    
    
    print MyClass.Foo
    print MyClass.Bar
    print str(MyClass)
    

    printing:

    foo!
    bar!
    custom str for MyClass
    

    And no, an object can't intercept a request for a stringifying one of its attributes. The object returned for the attribute must define its own __str__() behavior.

    0 讨论(0)
  • 2020-11-28 07:46

    Depending on the case I use this pattern

    class _TheRealClass:
        def __getattr__(self, attr):
           pass
    
    LooksLikeAClass = _TheRealClass()
    

    Then you import and use it.

    from foo import LooksLikeAClass
    LooksLikeAClass.some_attribute
    

    This avoid use of metaclass, and handle some use cases.

    0 讨论(0)
  • 2020-11-28 07:48

    For the first, you'll need to create a metaclass, and define __getattr__() on that.

    class MyMetaclass(type):
      def __getattr__(self, name):
        return '%s result' % name
    
    class MyClass(object):
      __metaclass__ = MyMetaclass
    
    print MyClass.Foo
    

    For the second, no. Calling str(MyClass.Foo) invokes MyClass.Foo.__str__(), so you'll need to return an appropriate type for MyClass.Foo.

    0 讨论(0)
  • 2020-11-28 07:59

    Surprised no one pointed this one out:

    class FooType(type):
        @property
        def Foo(cls):
            return "foo!"
    
        @property
        def Bar(cls):
            return "bar!"
    
    class MyClass(metaclass=FooType):
        pass
    

    Works:

    >>> MyClass.Foo
    'foo!'
    >>> MyClass.Bar
    'bar!'
    

    (for Python 2.x, change definition of MyClass to:

    class MyClass(object):
        __metaclass__ = FooType
    

    )

    What the other answers say about str holds true for this solution: It must be implemented on the type actually returned.

    0 讨论(0)
  • 2020-11-28 08:06

    (I know this is an old question, but since all the other answers use a metaclass...)

    You can use the following simple classproperty descriptor:

    class classproperty(object):
        """ @classmethod+@property """
        def __init__(self, f):
            self.f = classmethod(f)
        def __get__(self, *a):
            return self.f.__get__(*a)()
    

    Use it like:

    class MyClass(object):
    
         @classproperty
         def Foo(cls):
            do_something()
            return 1
    
         @classproperty
         def Bar(cls):
            do_something_else()
            return 2
    
    0 讨论(0)
提交回复
热议问题