我有一个任务:
实现元类" ModelCreator",允许声明类 以下形式的字段:
class Student(object): __metaclass__ = ModelCreator name = StringField()
其中
StringField
- 表示此字段的某个对象 文本域。所以必须有一个类,其构造函数接收 命名参数" name"并将其存储在相应的属性中(使用 类型检查和演员)所以你可以输入这样的东西:
s = Student(name = 'abc') print s.name
该类应该允许继承,并且应该验证类型 方式,你不能写一个数字到文本字段。
这是我的实现,但是继承类的问题是"" name"字段不为空(正如我所期望的那样)它从先前的类中获取名称值。
class StringField(object):
def __init__(self, my_type, default=None):
self.type = my_type
self.name = None
if default:
self.default = default
else:
self.default = my_type()
def __set__(self, instance, value):
if isinstance(value, self.type):
setattr(instance, self.name, value)
else:
raise TypeError("value must be type of {}".format(self.type))
def __get__(self, instance, owner):
return getattr(instance, self.name, self.default)
def __del__(self):
raise AttributeError("you can`t remove this attribute {}".format(self.name))
class ModelCreator(type):
def __new__(mcs, name, bases, diction):
socket = []
for key, value in diction.iteritems():
if isinstance(value, StringField):
value.name = "_{}".format(key)
socket.append(value.name)
def new(cls, *args, **kwargs):
for names in kwargs:
if '_{}'.format(names) in diction['__slots__']:
if isinstance(kwargs[names], diction[names].type):
diction[names].default = kwargs[names]
else:
raise TypeError('attr has other type')
return type(name, bases, diction)
diction["__slots__"] = socket
diction["__new__"] = new
return super(ModelCreator, mcs).__new__(mcs, name, bases, diction)
class Student(object):
__metaclass__ = ModelCreator
name = StringField(str)
class School(Student):
second_name = StringField(str)
def main():
st = Student(name = "Hello")
print st.name
st.name = "Vlad"
sc = School(second_name = "World")
print sc.second_name, sc.name
if __name__ == '__main__':
main()
此代码打印
您好
世界你好
但它应该(通过任务)打印
您好
世界无
问题是:
为什么类型(st)返回"键入' type' &#34 ;? (我认为这不是一个班级) 为什么类型(sc)返回" class' main .ModelCreator' "
如何确定" name"的价值。 "学生"中的字段class,所以它只会保存在" st" (因为它现在以某种方式包含在" sc")?
答案 0 :(得分:2)
这段代码有点令人费解 - 但它没有比你告诉它做的更好。
它做了什么,除了需要的描述符(即:包含__get__
和__set__
方法的类)和通常的元类机制之外,还将__new__
方法插入到类中在几个方面是错误的。
首先,分配给类new
的{{1}}方法通过使用对__new__
的硬编码调用结束其执行 - 这是最错误的 - 作为类型返回一个新类 - 而不是实例。插入的type.
方法结尾处的调用应该是new
- 或者更好,使用一种机制可以调用其object.__new__
下一个类中的__new__
(但这不会是微不足道的 - 因为你必须在围绕你插入的__mro__
方法的元类__new__
代码中找到它。
无论如何 - 如果你想要使用这个元类的类本身就是“类工厂”,那么只调用new
是有意义的 - 这不仅会返回声明字段的全新类,而且还会返回发送的默认值。调用类型是您看到type
返回type(st)
的原因 - 这是您的第一个问题。
然后,它仍然是错误的:在每个瞬间调用的type
类方法将默认属性设置为descritor(即“field”) - 并且该默认值将应用于每个其他实例化同一个类 - 或从其继承的其他类。您应该在调用new
类时设置默认值(如果有),并在将成为类StringField
的方法上设置实例的值。
如果您首先调用超类__new__
来获取实际实例,然后循环传入的关键字参数,并使用__new__
作为设置属性的机制,则可以执行此操作。使用setattr将确保正确调用StringField setattr
方法。
所以,这段代码中有许多奇怪的东西,但尝试修复它的方法是重新编写你的元类__set__
或多或少:
__new__
那就是说,你现在不应该浪费你的时间来研究Python 2.7中的这些高级机制(2017年) - Python 2最后一个版本是在2010年,它将在2020年停止维护 - 这些机制已经改进并且在3.x系列中得到了更好的表现。在Python 3.6中,使用 def __new__(mcs, name, bases, diction):
socket = set()
# mechanism to inherit classes that make use of sockets:
for base in bases:
if hasattr(base, '__slots__'):
socket.update(base.__slots__)
for key, value in diction.iteritems():
if isinstance(value, StringField):
value.name = "_{}".format(key)
socket.add(value.name)
def __new__(cls, *args, **kwargs):
# A working __new__ mechanism that respects inheritance.
for supercls in cls.__mro__[1:]:
if '__new__' in supercls.__dict__:
# don't pass args and kwargs up.
# unless really there is distinct mechanism
# to use args and kwargs than the StringField
# class.
# otherwise this would break any `__init__`
# method you put in your classes.
instance = supercls.__new__(cls)
break # the last class in __mro__ is object which always has '__new__'
for names in kwargs:
if '_{}'.format(names) in cls.__slots__:
# no need to typecheck here. StringField's __set__ does that
setattr(instance, kwargs[names])
return instance
diction["__slots__"] = list(socket)
diction["__new__"] = __new__
return super(ModelCreator, mcs).__new__(mcs, name, bases, diction)
描述符功能和新的__set_name__
机制,您甚至不需要使用自定义元类来获得此处的预期结果。