Вы должны искать метод от типа и передать в первом (self
) аргумент вручную:
type(Klass).get(Klass, 'arg')
Эта проблема является очень причина, по которой special method names are looked up using this path; пользовательские классы не будут хешировать или представлять себя, если Python этого не сделает.
Вы можете использовать этот факт ; а не использовать метод get()
, используйте __getitem__
, перегрузку [..]
синтаксиса индексации, и есть Python сделать type(ob).methodname(ob, *args)
танец для вас:
class Meta(type):
def __getitem__(self, arg):
pass
class 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 ...
Вы также мог бы справиться с этим в descriptor что метод подражает объекты:
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'
Вы думали о переопределении '__getattribute__' и выполнении некоторой проверки в этой логике? –
Какой '__getattribute__'? – linkyndy
'Met .__ getattribute__'. Поскольку '__getattribute__' вызывается * перед * dict lookup (если я не ошибаюсь). –