您必须在类型上查找方法,然后
self手动传入first()参数:
type(Klass).get(Klass, 'arg')
这个问题正是使用此路径查找特殊方法名称的原因; 如果Python不这样做,则自定义类本身将不可哈希或不可表示。
您 可以
利用这一事实;而不是使用
get()方法,而是使用
__getitem__,重载
[..]索引语法并让Python
type(ob).methodname(ob,*args)为您起舞:
class meta(type): def __getitem__(self, arg): passclass Klass(object): __metaclass__ = meta def __getitem__(self, arg): pass
然后
Klass()['arg']和
Klass['arg']正常工作。
但是,如果您必须
Klass.get()采取不同的行为(并且被拦截
meta.__getattribute__),则必须在您的
Klass.get方法中明确处理此问题;如果在类上调用它,则将减少一个参数,您可以利用它并返回对该类的调用:
_sentinel = object()class Klass(object): __metaclass__ = meta def get(self, arg=_sentinel): if arg=_sentinel: if isinstance(self, Klass): raise TypeError("get() missing 1 required positional argument: 'arg'") return type(Klass).get(Klass, self) # handle the instance case ...您还可以在模仿方法对象的描述符中处理此问题:
class class_and_instance_method(object): def __init__(self, func): self.func = func def __get__(self, instance, cls=None): if instance is None: # return the metaclass method, bound to the class type_ = type(cls) return getattr(type_, self.func.__name__).__get__(cls, type_) return self.func.__get__(instance, cls)
并将其用作装饰器:
class Klass(object): __metaclass__ = meta @class_and_instance_method def get(self, arg): pass
如果没有实例绑定到,它将把查找重定向到元类:
>>> class meta(type):... def __getattr__(self, name):... print 'meta.{} look-up'.format(name)... return lambda arg: arg... >>> class Klass(object):... __metaclass__ = meta... @class_and_instance_method... def get(self, arg):... print 'Klass().get() called'... return 'You requested {}'.format(arg)... >>> Klass().get('foo')Klass().get() called'You requested foo'>>> Klass.get('foo')meta.get look-up'foo'装饰器的应用可以在元类中完成:
class meta(type): def __new__(mcls, name, bases, body): for name, value in body.iteritems(): if name in proxied_methods and callable(value): body[name] = class_and_instance_method(value) return super(meta, mcls).__new__(mcls, name, bases, body)
然后您可以使用此元类将方法添加到类中,而不必担心委托:
>>> proxied_methods = ('get',)>>> class meta(type):... def __new__(mcls, name, bases, body):... for name, value in body.iteritems():... if name in proxied_methods and callable(value):... body[name] = class_and_instance_method(value)... return super(meta, mcls).__new__(mcls, name, bases, body)... def __getattr__(self, name):... print 'meta.{} look-up'.format(name)... return lambda arg: arg... >>> class Klass(object):... __metaclass__ = meta... def get(self, arg):... print 'Klass().get() called'... return 'You requested {}'.format(arg)... >>> Klass.get('foo')meta.get look-up'foo'>>> Klass().get('foo')Klass().get() called'You requested foo'


