I думаю это учитывает все случаи:
def isclass(obj):
try:
class_ = obj.__class__
except AttributeError:
# It's an old-style class
return True
try:
class_.__class__
except AttributeError:
# It's an old-style instance
return False
# It's new-style
return isinstance(obj, type)
Теперь давайте проверим его, чтобы убедиться, что он работает как положено / ожидаемо:
def test():
class OldStyle: pass
class NewStyle(object): pass
def class_factory():
return type("DynamicClass", (object,), {})
class MetaClass(type):
def __new__(mcls, *args):
if args:
return type(*args)
return class_factory()
class WithMeta(object):
__metaclass__ = MetaClass
def __call__(self):
return self
confusing = WithMeta()
for name, obj in locals().items():
templ = "{0:>26s} == {1!r:>5}, {2:>26s} == {3!r:>5}"
print templ.format(
"isclass({0})".format(name), isclass(obj),
"isclass({0}())".format(name), isclass(obj()))
Вывод:
isclass(OldStyle) == True, isclass(OldStyle()) == False
isclass(NewStyle) == True, isclass(NewStyle()) == False
isclass(WithMeta) == True, isclass(WithMeta()) == False
isclass(MetaClass) == True, isclass(MetaClass()) == True
isclass(class_factory) == False, isclass(class_factory()) == True
isclass(confusing) == False, isclass(confusing()) == False
MetaClass
, class_factory
и confusing
иллюстрируют дискуссию о том, что составляет класс в python, и показывают, как функция isclass
обрабатывает эти случаи.
Re: agf
MetaClass
не предназначен для такой реализации, поэтому ваши уловки, чтобы избежать ошибки, действительно просто запутывают проблему;правильнее было бы печатать «Ошибка» для MetaClass()
.
Вы или я написали MetaClass
?Я имел в виду, что именно так и должны быть созданы.Я признаю, что нет никакого смысла иметь метакласс (или функцию фабрики классов), которая создает «один и тот же» класс при каждом его вызове, и, конечно, это сбивает с толку.Однако для этих конкретных целей я хотел бы проиллюстрировать, что метакласс создает класс при создании его экземпляра, и наличие множества аргументов для MetaClass()
будет комбо-прерывателем в выходных данных.:) Так что в принципе я согласен с вами, но для этой конкретной цели я делаю это таким образом, и python позволяет мне это делать.
__call__
происходит ДО __new__
и что такое WithMeta()
, confusing
, и confusing()
шоу корректно.
Если вы под словом «случается» подразумеваете «проанализированы, скомпилированы и связаны», тогда да, это происходит первым.Я не уверен, что понимаю, что ты здесь делаешь.Я согласен, что это правильно - конечно, это правильно, этот код был написан и запущен, и я опубликовал полученные результаты, так что это не может не быть правильным.Я не вижу, что с этим связано __call__
.Не могли бы вы уточнить?
Вы на самом деле никогда не создавали WithMeta
.Вы должны просто удалить это __call__
и показать ошибку для confusing()
Я вполне уверен, что я делаю создаю экземпляр WithMeta
:
>>> WithMeta()
<isclass.WithMeta object at 0xb784574c>
Когда я пишу WithMeta()
, python проверит, определено ли type(WithMeta).__new__
, и это так, поэтому python затем предоставляет имя класса, базы и dict пространства имен в качестве аргументов для MetaClass.__new__()
.Он возвращает объект класса WithMeta
, который создается путем вызова type
с теми же аргументами.
С другой стороны, confusing()
совпадает с type(confusing).__dict__["__call__"](confusing)
, который просто возвращает аргумент,так что confusing == confusing()
.