Вот пример игрушек для создания декоратора, который позволяет объявлять имена атрибутов, которые должны быть обязательными для частей «проверки интерфейса» по стандартным шаблонам __subclasshook__
и __instancecheck__
.Зачем «__subclasshook__» быть обезврежен в метаклассе, но `__instancecheck__` не может?
Кажется, что работаю как ожидалось, когда я украшаю класс Foo
. Я делаю класс Bar
, не имеющий отношения к Foo
, но который имеет необходимые атрибуты, и он правильно удовлетворяет isinstance(instance_of_bar, Foo) == True
.
Но тогда как еще один пример, я сделать подкласс dict
дополненной так что key
значения будут доступны с getattr
синтаксиса, а также (например, dict
где d['a']
можно заменить d.a
, чтобы получить тот же результат). В этом случае атрибуты являются только атрибутами экземпляра, поэтому __instancecheck__
должен работать.
Вот код. Обратите внимание, что, учитывая, что работает пример с Bar
, выбор функции «monkeypatch» __subclasshook__
в класс Foo
(который имеет метакласс) отлично работает. Так что не кажется, что нужно определить функцию непосредственно в определении класса метакласса.
#Using Python 2.7.3
import abc
def interface(*attributes):
def decorator(Base):
def checker(Other):
return all(hasattr(Other, a) for a in attributes)
def __subclasshook__(cls, Other):
if checker(Other):
return True
return NotImplemented
def __instancecheck__(cls, Other):
return checker(Other)
Base.__subclasshook__ = classmethod(__subclasshook__)
Base.__instancecheck__ = classmethod(__instancecheck__)
return Base
return decorator
@interface("x", "y")
class Foo(object):
__metaclass__ = abc.ABCMeta
def x(self): return 5
def y(self): return 10
class Bar(object):
def x(self): return "blah"
def y(self): return "blah"
class Baz(object):
def __init__(self):
self.x = "blah"
self.y = "blah"
class attrdict(dict):
def __getattr__(self, attr):
return self[attr]
f = Foo()
b = Bar()
z = Baz()
t = attrdict({"x":27.5, "y":37.5})
print isinstance(f, Foo)
print isinstance(b, Foo)
print isinstance(z, Foo)
print isinstance(t, Foo)
Печать:
True
True
False
False
Это просто игрушка пример - я не ищу лучшие способы для реализации моего attrdict
класса. Пример Bar
демонстрирует работу с обезьяной __subclasshook__
. В двух других примерах демонстрируется отказ __instancecheck__
для экземпляров, которые имеют простые атрибуты экземпляра для проверки. В таких случаях __instancecheck__
даже не называется.
я могу вручную проверить, что условие из моей __instancecheck__
функции выполняется экземпляром attrdict
(то есть, hasattr(instance_of_attrdict, "x")
является True
по мере необходимости) или z
.
Опять же, похоже, что это нормально для экземпляра Bar
. Это говорит о том, что __subclasshook__
правильно применяется декоратором и что исправление пользовательского __metaclass__
не является проблемой. Но __instancecheck__
, похоже, не вызван в процессе.
Почему можно определить __subclasshook__
за пределами определения класса метакласса и добавлено позже, но не __instancecheck__
?
Это на самом деле не имеет ничего общего с встроенными типами. Параметр 'Other' для' __subclasshook__' является * классом *, а не экземпляром класса. Если вы заменили 'attrdict' классом с именем' Baz', который присвоил 'self.x' и' self.y' в своем методе '__init__', вы получите точные результаты, потому что сам класс не имеет таких атрибуты, но экземпляр этого делает. – Blender
Но почему бы не '__subclasshook__' (в силу' hasattr (Other, a) 'return' False', поскольку я неправильно пытаюсь использовать 'Other', как экземпляр вместо класса), просто верните' False', вместо этого из «AssertionError», который я вижу. Затем следует следующее: как я могу использовать '__subclasshook__' для определения проверки того, имеют ли экземпляры * необходимые атрибуты, которые будут считаться экземпляром, независимо от того, были ли эти атрибуты обезврежены, созданы как атрибуты класса, созданные как атрибуты экземпляра в '__init__' или что-то еще. – ely
Ваш код не должен работать в первую очередь, так как 'all()' возвращает логическое значение, которое не имеет метода 'next()'. «AssertionError» просто говорит, что '__subclasshook__' ожидает, что тип возвращаемого значения будет логическим, в то время как вы возвращаете что-то еще. '__instancecheck__' - это то, что вы действительно должны реализовывать. – Blender