关于 Python 的元类,为什么设置了__metaclass__不起作用?

2019-03-28 16:51:38 +08:00
 Hopetree

说实话,写的 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']

1818 次点击
所在节点    Python
4 条回复
lllmlll
2019-03-28 16:58:16 +08:00
class Meta(type):
pass

class MyClass(metaclass=Meta):
pass

class MySubclass(MyClass):
pass

Python3 元类使用方式不一样,你感受下
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."
Hopetree
2019-03-28 17:04:30 +08:00
@lllmlll
@u14e
那就对了,你们看我下面自己写的代码

class Student2(unittest.TestCase, metaclass=change_name):

class Student3(unittest.TestCase, metaclass=Meta):

难怪,我就说怎么我设置__metaclass__不起作用,果然是 Python2 和 Python3 的使用方式不同的问题

感谢回复
shyrock
2019-03-28 18:36:57 +08:00
py3 引入的 metaclass=,py2 用的__metaclass__。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/549596

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX