说实话,写的 Python 代码也不少,小到脚本、爬虫,大到 web 框架,但是写来写去,却连最原始的东西也还没有搞清楚,今天看了很多关于 Python 元类的文章,也领悟了一些东西,但是还是有疑问。
背景:我今天看了好几遍文章,博客园+简书,都是关于元类的,但是我发现这几篇高排名的文章好像写的东西很类似,连例子都是一样的,我开始以为是同一个人写的,后来发现原来原始的例子在 stackoverflow 中,看来都是从这里学习之后自己分享的。
其他的例子我都能懂,但是下面这段代码,我 Python3 运行却不是预期的输出,难道下面的代码是 Python2 才能按照注释里面的预期输出吗?
def upper_attr(future_class_name, future_class_parents, future_class_attr):
"""
Return a class object, with the list of its attribute turned
into uppercase.
"""
# pick up any attribute that doesn't start with '__' and uppercase it
uppercase_attr = {}
for name, val in future_class_attr.items():
if not name.startswith('__'):
uppercase_attr[name.upper()] = val
else:
uppercase_attr[name] = val
# let `type` do the class creation
return type(future_class_name, future_class_parents, uppercase_attr)
__metaclass__ = upper_attr # this will affect all classes in the module
class Foo(): # global __metaclass__ won't work with "object" though
# but we can define __metaclass__ here instead to affect only this class
# and this will work with "object" children
bar = 'bip'
print(hasattr(Foo, 'bar'))
# Out: False
print(hasattr(Foo, 'BAR'))
# Out: True
我 Python3 的结果是
True
False
这让我发现设置__metaclass__根本没有起作用,所以,我的问题是,__metaclass__到底怎么使用???????????
有兴趣的还可以看看我自己的代码(我在自定义一个元类,用途是给 unittest 的用例添加序号)
import unittest
LONG = 5
class Meta(type):
def __new__(cls, class_name, class_parents, class_attrs):
id = 1
_others = {}
_attrs = {}
for k, v in class_attrs.items():
if k.startswith('__') and k.endswith('__'):
_others[k] = v
else:
if k.startswith('test_'):
k = k.replace('test_', "test_{}_".format(str(id).zfill(LONG)))
id += 1
_attrs[k] = v
_attrs.update(_others)
return type.__new__(cls, class_name, class_parents, _attrs)
def change_name(cls_name, cls_parents, cls_attrs):
id = 1
_others = {}
_attrs = {}
for k, v in cls_attrs.items():
if k.startswith('__') and k.endswith('__'):
_others[k] = v
else:
if k.startswith('test_'):
k = k.replace('test_', "test_{}_".format(str(id).zfill(LONG)))
id += 1
_attrs[k] = v
_attrs.update(_others)
return type(cls_name, cls_parents, _attrs)
class Student1(unittest.TestCase):
def test_kkk(self):
return 1
def test_bbb(self):
return 3
class Student2(unittest.TestCase, metaclass=change_name):
def test_kkk(self):
return 1
def test_bbb(self):
return 3
class Student3(unittest.TestCase, metaclass=Meta):
def test_kkk(self):
self.assertEqual(1, 1)
def test_bbb(self):
self.assertEqual(1,1)
class Student4(unittest.TestCase):
__metaclass__ = Meta
def test_kkk(self):
self.assertEqual(1, 1)
def test_bbb(self):
self.assertEqual(1,1)
print(dir(Student1))
print(dir(Student2))
print(dir(Student3))
print(dir(Student4))
上面的 4 个打印结果的最后一段是下面这样的,2 和 3 符合预期结果,但是 4 不符合,4 的结果跟我问的一样,我发现我设置__metaclass__属性根本不起作用。
[... 'test_bbb', 'test_kkk']
[... 'test_00001_bbb', 'test_00002_kkk']
[... 'test_00001_bbb', 'test_00002_kkk']
[... 'test_bbb', 'test_kkk']
1
lllmlll 2019-03-28 16:58:16 +08:00
class Meta(type):
pass class MyClass(metaclass=Meta): pass class MySubclass(MyClass): pass Python3 元类使用方式不一样,你感受下 |
2
u14e 2019-03-28 17:01:14 +08:00
原文有说过啊:
"i.e. the __metaclass__ attribute is no longer used, in favor of a keyword argument in the list of base classes." |
3
Hopetree OP |
4
shyrock 2019-03-28 18:36:57 +08:00
py3 引入的 metaclass=,py2 用的__metaclass__。
|