Python dynamic class methods

匿名 (未验证) 提交于 2019-12-03 00:50:01

问题:

Say there is:

class A(B):     ... 

where B could be object and ... is not:

@classmethod # or @staticmethod def c(cls): print 'Hello from c!' 

What do I have to do that calling A.c() wont trigger AttributeError?

In other words, I know it is possible to manually add class methods to a class at runtime. But is it possible to do so automatically, say every time a class method is missing it creates some dummy method?

In yet another words, only if I could replace A.__dict__ with my dict which handles __getitem__ - but A.__dict__ seems to be not writable...

回答1:

You can achieve this by using a __getattr__ hook on a metaclass.

class DefaultClassMethods(type):     def __getattr__(cls, attr):         def _defaultClassMethod(cls):             print 'Hi, I am the default class method!'         setattr(cls, attr, classmethod(_defaultClassMethod))         return getattr(cls, attr) 

Demo:

>>> class DefaultClassMethods(type): ...     def __getattr__(cls, attr): ...         def _defaultClassMethod(cls): ...             print 'Hi, I am the default class method!' ...         setattr(cls, attr, classmethod(_defaultClassMethod)) ...         return getattr(cls, attr) ...  >>> class A(object): ...     __metaclass__ = DefaultClassMethods ...  >>> A.spam > >>> A.spam() Hi, I am the default class method! 

Note that we set the result of the classmethod call straight onto the class, effectively caching it for future lookups.

If you need to regenerate the class method on every call instead, use the same method to bind a function to an instance but with the class and metaclass instead (using cls.__metaclass__ to be consistent with metaclass subclassing):

from types import MethodType  class DefaultClassMethods(type):     def __getattr__(cls, attr):         def _defaultClassMethod(cls):             print 'Hi, I am the default class method!'         return _defaultClassMethod.__get__(cls, cls.__metaclass__) 

For static methods just return the function directly in all cases, no need to muck with the staticmethod decorator or the descriptor protocol.



回答2:

The behaviors provided to instances by methods like __getattr__ and the descriptor protocol can work for classes as well, but in that case, you have to code them in the class's metaclass.

In this case, all one needs to do is to set the metaclass __getattr__ function to auto-generate the desired class attribute.

(The setattr, getattr trick is to let Python do the function->method transoform with no need to mess with it)

class AutoClassMethod(type):     def __getattr__(cls, attr):         default = classmethod(lambda cls: "Default class method for " + repr(cls))         setattr(cls, attr, default)         return getattr(cls, attr)    class A(object):     __metaclass__ = AutoClassMethod     @classmethod     def b(cls):         print cls 


回答3:

>>> class C(object): ...     pass ...  >>> C.m = classmethod(lambda cls: cls.__name__) >>> C.m() 'C' 

Or you can use somethigs like this:

class Wrapper(object):      def __init__(self, clz, default=lambda cls: None):         self._clz = clz         self._default = default      def __getattr__(self, attr):         # __attrs__ will be getted from Wrapper         if attr.startswith('__'):             return self.__getattribute__(attr)          if not hasattr(self._clz, attr):             setattr(self._clz, attr, classmethod(self._default))         return getattr(self._clz, attr)      def __call__(self, *args, **kwargs):         return self._clz(*args, **kwargs)  >>> class C(object): ...     pass ... >>> C = Wrapper(C, default=lambda cls: cls.__name__) >>> c = C() >>> print C.m() 'C' >>> print c.m() # now instance have method "m" 'C' 


标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!