V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
Hopetree
V2EX  ›  Python

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

  •  
  •   Hopetree ·
    Hopetree · 2019-03-28 16:51:38 +08:00 · 1992 次点击
    这是一个创建于 2101 天前的主题,其中的信息可能已经有所发展或是发生改变。

    说实话,写的 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']
    
    
    lllmlll
        1
    lllmlll  
       2019-03-28 16:58:16 +08:00
    class Meta(type):
    pass

    class MyClass(metaclass=Meta):
    pass

    class MySubclass(MyClass):
    pass

    Python3 元类使用方式不一样,你感受下
    u14e
        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."
    Hopetree
        3
    Hopetree  
    OP
       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
        4
    shyrock  
       2019-03-28 18:36:57 +08:00
    py3 引入的 metaclass=,py2 用的__metaclass__。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2909 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 11:20 · PVG 19:20 · LAX 03:20 · JFK 06:20
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.