为什么元类会改变 issubclass() 的工作方式?
好的,所以我正在编写一个框架,该框架在名为 task.py
的子目录中查找 python 文件,然后查找从基类 Task
派生的类并收集它们。 我决定需要向 Task
添加一个元类,但随后 issubclass()
开始表现得很奇怪。 布局如下:
start.py
tasks/__init__.py
tasks/base.py
tasks/sub/__init__.py # empty
tasks/sub/task.py
start.py:tasks/init.pytasks/base.pytasks/sub/task.py
#!/usr/bin/env python
from tasks.base import Task1, Task2
from tasks.sub.task import SubTask1, SubTask2
print "Normal import:"
print "is SubTask1 sub class of Task1? %s" % issubclass(SubTask1, Task1)
print "is SubTask2 sub class of Task2? %s" % issubclass(SubTask2, Task2)
from tasks import setup
print "Imp import:"
setup()
我
import os.path, imp, types
from tasks.base import Task1, Task2
# Find all task definitions
ALL_TASK1 = { }
ALL_TASK2 = { }
def _append_class(d, task, base):
if (type(task) == types.TypeType) and issubclass(task, base):
if task == base:
return
if not task.name in d:
d[task.name] = task
this_dir = os.path.dirname(os.path.abspath(__file__))
for root, dirs, files in os.walk(this_dir):
if not "task.py" in files:
continue
mod_name = "task"
f, pathname, description = imp.find_module(mod_name, [ root ])
m = imp.load_module(mod_name, f, pathname, description)
f.close()
for task in m.__dict__.itervalues():
_append_class(ALL_TASK1, task, Task1)
_append_class(ALL_TASK2, task, Task2)
def setup():
print "All Task1: %s" % ALL_TASK1
print "All Task2: %s" % ALL_TASK2
,
class MetaClass (type):
def __init__(cls, name, bases, attrs):
pass
class Base (object):
__metaclass__ = MetaClass
def method_using_metaclass_stuff(self):
pass
class Task1 (Base):
pass
class Task2 (object):
pass
目录
from tasks.base import Task1, Task2
class SubTask1 (Task1): # Derived from the __metaclass__ class
name = "subtask1"
class SubTask2 (Task2):
name = "subtask2"
当我运行 setup.py
时 得到以下输出(ALL_TASK1 字典为空!):
Normal import:
is SubTask1 sub class of Task1? True
is SubTask2 sub class of Task2? True
Imp import:
All Task1: {}
All Task2: {'subtask2': <class 'task.SubTask2'>}
但是当我注释掉类 Base
中的 __metaclass__
行(Task1
's base类)我得到了预期的输出(ALL_TASK1 字典不为空):
Normal import:
is SubTask1 sub class of Task1? True
is SubTask2 sub class of Task2? True
Imp import:
All Task1: {'subtask1': <class 'task.SubTask1'>}
All Task2: {'subtask2': <class 'task.SubTask2'>}
我不明白当通过 imp< 导入模块时,元类如何影响
issubclass()
/code> 函数,但当使用普通 import
导入模块时则不起作用。
有人可以向我解释一下吗(我正在使用 python 2.6.1)?
OK, so I'm writing a framework that looks for python files in sub directories that are named task.py
and then look for classes that are derived from the base class Task
and collect them.
I decided that I needed to add a meta class to Task
, but then the issubclass()
started to behave in a weird way.
Here is how the directory layout looks:
start.py
tasks/__init__.py
tasks/base.py
tasks/sub/__init__.py # empty
tasks/sub/task.py
start.py:
#!/usr/bin/env python
from tasks.base import Task1, Task2
from tasks.sub.task import SubTask1, SubTask2
print "Normal import:"
print "is SubTask1 sub class of Task1? %s" % issubclass(SubTask1, Task1)
print "is SubTask2 sub class of Task2? %s" % issubclass(SubTask2, Task2)
from tasks import setup
print "Imp import:"
setup()
tasks/init.py
import os.path, imp, types
from tasks.base import Task1, Task2
# Find all task definitions
ALL_TASK1 = { }
ALL_TASK2 = { }
def _append_class(d, task, base):
if (type(task) == types.TypeType) and issubclass(task, base):
if task == base:
return
if not task.name in d:
d[task.name] = task
this_dir = os.path.dirname(os.path.abspath(__file__))
for root, dirs, files in os.walk(this_dir):
if not "task.py" in files:
continue
mod_name = "task"
f, pathname, description = imp.find_module(mod_name, [ root ])
m = imp.load_module(mod_name, f, pathname, description)
f.close()
for task in m.__dict__.itervalues():
_append_class(ALL_TASK1, task, Task1)
_append_class(ALL_TASK2, task, Task2)
def setup():
print "All Task1: %s" % ALL_TASK1
print "All Task2: %s" % ALL_TASK2
tasks/base.py
class MetaClass (type):
def __init__(cls, name, bases, attrs):
pass
class Base (object):
__metaclass__ = MetaClass
def method_using_metaclass_stuff(self):
pass
class Task1 (Base):
pass
class Task2 (object):
pass
tasks/sub/task.py
from tasks.base import Task1, Task2
class SubTask1 (Task1): # Derived from the __metaclass__ class
name = "subtask1"
class SubTask2 (Task2):
name = "subtask2"
When I run setup.py
I got the following output (ALL_TASK1 dict is empty!):
Normal import:
is SubTask1 sub class of Task1? True
is SubTask2 sub class of Task2? True
Imp import:
All Task1: {}
All Task2: {'subtask2': <class 'task.SubTask2'>}
But when I comment out the __metaclass__
line in the class Base
(Task1
's base class) I got the output I expected (ALL_TASK1 dict isn't empty):
Normal import:
is SubTask1 sub class of Task1? True
is SubTask2 sub class of Task2? True
Imp import:
All Task1: {'subtask1': <class 'task.SubTask1'>}
All Task2: {'subtask2': <class 'task.SubTask2'>}
I don't understand how the meta class can affect issubclass()
when the module is imported via the imp
functions, but not when the module is imported with the normal import
.
Can someone explain this to me (I'm using python 2.6.1)?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
您的代码也无法通过正常导入运行。
当您实例化元类(作为类)时,实例的类型不是
TypeType
,而是tasks.base.MetaClass
类型。如果您除了TypeType
之外还对其进行测试,那么您的代码将按预期工作。这会产生与注释掉元类行相同的输出,并且比原始代码有一个优势,因为它允许您的用户定义自己的元类并让它们作为任务运行。如果您想明确禁止这一点(请不要这样做,有一天我可能想使用您的框架)您可以专门检查您的元类或
TypeType
。Your code doesn't work via normal import either.
When you instantiate the metaclass (as a class), the instance doesn't have type
TypeType
, it has typetasks.base.MetaClass
. if you test for that in addition toTypeType
, then your code works as expected.This yields the same output as when you commented out the metaclass line and has an advantage over your origninal code in that it allows your users to define their own metaclasses and have them function as tasks. If you want to explicitly dissallow this (and please don't, I might want to use your framework someday) you can just check for both your metaclass or
TypeType
specifically.当您在
Base
中定义__metaclass__
时,type(Base)
变为tasks.base.MetaClass
,而不是 <代码>类型.TypeType。在
_append_class
中,您真正关心的似乎只是获取那些属于基类子类的任务。因此,您可以只测试 issubclass(task,base) 并捕获异常,否则:
When you define
__metaclass__
inBase
, thetype(Base)
becomestasks.base.MetaClass
, rather thantypes.TypeType
.It seems in
_append_class
that all you really care about is grabbing those tasks which are subclasses of base. So instead ofyou could just test
issubclass(task,base)
and catch exceptions otherwise: