我们如何根据元类中 init 传递的属性来改变属性

Posted

技术标签:

【中文标题】我们如何根据元类中 init 传递的属性来改变属性【英文标题】:How can we mutate attributes on basis of attributes passed at init in metaclass 【发布时间】:2019-03-11 00:58:09 【问题描述】:

假设我有一个类,它带有一个名为 bucket_name 的属性,基于这个桶,我想为类设置一个属性 bucket_path,它是一种 bucket_path = "bucket_name_created".format(bucket_path='master-bucket')

我正在尝试使用元类,如下所示:

class MyMeta(type):
    def __new__(meta, klassname, bases, attrs):
        # trying to get bucket_name here from attrs but attrs is 
        return type.__new__(meta, klassname, bases, attrs)

class M(object):
    __metaclass__= MyMeta
    def __init__(self, bucket_name):
        self.bucket_name = bucket_name

但我失败了,因为当我执行 m = M('my_bucket_name') 时 attrs 为空, 怎么办?

【问题讨论】:

不可能,因为MyMeta(... initalize bevor M.__init__(...。除此之外,您将bucket_name 传递给M.__init__(... 而不是MyMeta.__new__(...。为什么不Inheritance,在基类中创建你的bucket_path 【参考方案1】:

属性bucket_name是一个instance属性,因此bucket_path不可能同时依赖于bucket_name又是一个类属性。

两者都必须是类属性或实例属性。

实例属性

如果两者都是实例属性,则不需要元类,property 就足够了。

class M(object):
    def __init__(self, bucket_name):
        self.bucket_name = bucket_name

    @property
    def bucket_path(self):
        return "_created".format(self.bucket_name)

m = M('foo')
print(m.bucket_path) # foo_created

类属性

如果两者都是类属性,那么你要定义一个属性,但是在类上。虽然您可以define a class decorator to implement class properties,但这确实可以通过元类来实现。

class MyMeta(type):
    def __new__(cls, name, bases, namespace):
        if 'bucket_name' not in namespace:
            raise TypeError("class must have 'bucket_name'")

        # This is meant to also delegate the instance attribute to the class property
        namespace['bucket_path'] = property(lambda self: type(self).bucket_path)

        return super(MyMeta, cls).__new__(cls, name, bases, namespace)

    @property
    def bucket_path(cls):
        return "_created".format(cls.bucket_name)

class M(object):
    __metaclass__ = MyMeta
    bucket_name = 'foo'

print(M.bucket_path) # foo_created
print(M().bucket_path) # foo_created

【讨论】:

以上是关于我们如何根据元类中 init 传递的属性来改变属性的主要内容,如果未能解决你的问题,请参考以下文章

描述符get/set/delete,init/new/call,元类

如何在 Groovy 类中“隐藏”元类属性

3.1.18 元类的练习

面向对象的深刻理解

3.python元类编程

3.python元类编程