我们如何根据元类中 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 传递的属性来改变属性的主要内容,如果未能解决你的问题,请参考以下文章