我使用元类为这样的新类创建属性:
class Property(object):
def __init__(self, internal_name, type_, default_value):
self._internal_name = internal_name
self._type = type_
self._default_value = default_value
def generate_property(self):
def getter(object_):
return getattr(object_, self._internal_name)
def setter(object_, value):
if not isinstance(value, self._type):
raise TypeError("Expect type {0}, got {1}.".format(self._type, type(value)))
else:
setattr(object_, self._internal_name, value)
return property(getter, setter)
class AutoPropertyMeta(type):
def __new__(cls, name, bases, attributes):
for name, value in attributes.iteritems():
if isinstance(value, Property):
attributes[name] = value.generate_property()
return super(AutoPropertyMeta, cls).__new__(cls, name, bases, attributes)
通过这种方式,我可以编写如下代码:
class SomeClassWithALotAttributes(object):
__metaclass__ = AutoPropertyMeta
attribute_a = Property("_attribute_a", int, 0)
...
attribute_z = Property("_attribute_z", float, 1.0)
而不是:
class SomeClassWithALotAttributes(object):
def __init__(self):
self._attribute_a = 0
...
self._attribute_z = 1.0
def get_attribute_a(self):
return self._attribute_a
def set_attribute_a(self, value):
if not isinstance(value, int):
raise TypeError("Expect type {0}, got {1}.".format(self._type, type(value))
else:
self._attribute_a = value
attribute_a = property(get_attribute_a, set_attribute_a)
...
如果您始终在获取属性值之前设置值,则效果很好,因为AutoPropertyMeta仅生成getter
和setter
方法。第一次设置值时会创建实际的实例属性。所以我想知道是否有一种方法可以通过元类为类创建实例属性。
这是我现在使用的解决方法,但我总是想知道是否有更好的方法:
class Property(object):
def __init__(self, internal_name, type_, default_value):
self._internal_name = internal_name
self._type = type_
self._default_value = default_value
def generate_property(self):
def getter(object_):
return getattr(object_, self._internal_name)
def setter(object_, value):
if not isinstance(value, self._type):
raise TypeError("Expect type {0}, got {1}.".format(self._type, type(value)))
else:
setattr(object_, self._internal_name, value)
return property(getter, setter)
def generate_attribute(self, object_):
setattr(object_, self._internal_name, self._default_value)
class AutoPropertyMeta(type):
def __new__(cls, name, bases, attributes):
property_list = []
for name, value in attributes.iteritems():
if isinstance(value, Property):
attributes[name] = value.generate_property()
property_list.append(value)
attributes["_property_list"] = property_list
return super(AutoPropertyMeta, cls).__new__(cls, name, bases, attributes)
class AutoPropertyClass(object):
__metaclass__ = AutoPropertyMeta
def __init__(self):
for property_ in self._property_list:
property_.generate_attribute(self)
class SomeClassWithALotAttributes(AutoPropertyClass):
attribute_a = Property("_attribute_a", int, 0)
答案 0 :(得分:1)
以下是关于注入新__init__
的意思的示例。请注意,这只是为了好玩,你不应该这样做。
class Property(object):
def __init__(self, type_, default_value):
self._type = type_
self._default_value = default_value
def generate_property(self, name):
self._internal_name = '_' + name
def getter(object_):
return getattr(object_, self._internal_name)
def setter(object_, value):
if not isinstance(value, self._type):
raise TypeError("Expect type {0}, got {1}.".format(self._type, type(value)))
else:
setattr(object_, self._internal_name, value)
return property(getter, setter)
class AutoPropertyMeta(type):
def __new__(meta, name, bases, attributes):
defaults = {}
for name, value in attributes.iteritems():
if isinstance(value, Property):
attributes[name] = value.generate_property(name)
defaults[name] = value._default_value
# create __init__ to inject into the class
# our __init__ sets up our secret attributes
if '__init__' in attributes:
realInit = attributes['__init__']
# we do a deepcopy in case default is mutable
# but beware, this might not always work
def injectedInit(self, *args, **kwargs):
for name, value in defaults.iteritems():
setattr(self, '_' + name, copy.deepcopy(value))
# call the "real" __init__ that we hid with our injected one
realInit(self, *args, **kwargs)
else:
def injectedInit(self, *args, **kwargs):
for name, value in defaults.iteritems():
setattr(self, '_' + name, copy.deepcopy(value))
# inject it
attributes['__init__'] = injectedInit
return super(AutoPropertyMeta, meta).__new__(meta, name, bases, attributes)
然后:
class SomeClassWithALotAttributes(object):
__metaclass__ = AutoPropertyMeta
attribute_a = Property(int, 0)
attribute_z = Property(list, [1, 2, 3])
def __init__(self):
print("This __init__ is still called")
>>> x = SomeClassWithALotAttributes()
This __init__ is still called
>>> y = SomeClassWithALotAttributes()
This __init__ is still called
>>> x.attribute_a
0
>>> y.attribute_a
0
>>> x.attribute_a = 88
>>> x.attribute_a
88
>>> y.attribute_a
0
>>> x.attribute_z.append(88)
>>> x.attribute_z
[1, 2, 3, 88]
>>> y.attribute_z
[1, 2, 3]
>>> x.attribute_z = 88
Traceback (most recent call last):
File "<pyshell#76>", line 1, in <module>
x.attribute_z = 88
File "<pyshell#41>", line 12, in setter
raise TypeError("Expect type {0}, got {1}.".format(self._type, type(value)))
TypeError: Expect type <type 'list'>, got <type 'int'>.
我们的想法是编写自己的__init__
来执行秘密属性的初始化。然后在创建类之前将其注入类命名空间,但存储对原始__init__
(如果有)的引用,以便您可以在需要时调用它。