说实话,写的 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__。 |