实施特殊元class。继承中的 Nonify 字段 class
Implement special metaclass. Nonify field in inherited class
我有一个任务:
Implement metaclass "ModelCreator", that allows to declare class
fields in the following form:
class Student(object):
__metaclass__ = ModelCreator
name = StringField()
Where StringField
- some object that indicates, that this field is
text field. So there must be a class, whose constructor receives
named argument "name" and stores it in corresponding attribute (with
type check and cast)
So you can type something like this:
s = Student(name = 'abc')
print s.name
The class should allow inheritance and should verify the types such
way, that you cannot write a number to text field.
这是我的实现,但继承 class 存在问题,它的 "name" 字段不为空(如我所料)它从以前的 class 中接收名称值。
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()
此代码打印
Hello
World Hello
但它应该(按任务)打印
Hello
World None
问题是:
Why the type(st) returns " type 'type' "? (I thought it is instance not a class)
Why the type(sc) returns " class 'main.ModelCreator' "?
How to Nonify the value of "name" field in the "Student" class, so it will only be saved in "st" (cause it is now somehow contained even in "sc")?
此代码有点复杂 - 但它所做的只是您告诉它要做的事情。
除了所需的描述符(即:包含 __get__
和 __set__
方法的 class 和通常的元 class 机制外,它还做了什么class 的 __new__
方法在几个方面是错误的。
首先,分配给 class __new__
的 new
方法通过使用对 type.
的硬编码调用结束其执行 - 这是最错误的最重要的是 - 作为类型 returns 一个新的 class - 不是一个实例。插入的 new
方法末尾的调用应该是 object.__new__
- 或者更好的是,使用一种机制在下一个 class 中调用 __new__
__mro__
(但这并不是微不足道的 - 因为您必须在围绕您插入的 new
方法的元 class __new__
代码中找到它)。
无论如何——如果你想让使用这个元class的class成为他们自己"class factories",那么在那里调用type
才有意义——那会return 全新的 class 不仅包含声明的字段,还包含发送的默认值。呼叫类型是您看到 type(st)
returning type
的原因 - 这是您的第一个问题。
然后,它仍然是错误的:在每个实例化时调用的 new
class 方法将默认属性设置为描述符(即 "field") - 而且默认值将应用于相同 class 的所有其他实例化 - 或从它继承的其他 classes。您应该在对 StringField
class 的调用中设置默认值(如果有的话),并在将成为 class 上的 __new__
的方法上设置值实例。
如果你先调用 superclass __new__
来获得一个实际的实例,然后循环传入的关键字参数,并使用 setattr
作为设置属性的机制。使用 setattr 将确保正确调用 StringField __set__
方法。
所以,这段代码中有很多奇怪的地方,但尝试通过重写你的元class __new__
来修复它,或多或少:
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)
也就是说,你真的不应该浪费你的时间去研究 Python 2.7 中的这种高级机制,此时 (2017) - Python 2 最后一个版本是在 2010 年,它将是2020 年停止维护 - 这些机制在 3.x 系列中得到了改进和改进。在 Python 3.6 中,有了 __set_name__
描述符功能和新的 __init_subclass__
机制,您甚至不需要为此处的预期结果使用自定义元 class。
我有一个任务:
Implement metaclass "ModelCreator", that allows to declare class fields in the following form:
class Student(object): __metaclass__ = ModelCreator name = StringField()
Where
StringField
- some object that indicates, that this field is text field. So there must be a class, whose constructor receives named argument "name" and stores it in corresponding attribute (with type check and cast)So you can type something like this:
s = Student(name = 'abc') print s.name
The class should allow inheritance and should verify the types such way, that you cannot write a number to text field.
这是我的实现,但继承 class 存在问题,它的 "name" 字段不为空(如我所料)它从以前的 class 中接收名称值。
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()
此代码打印
Hello
World Hello
但它应该(按任务)打印
Hello
World None
问题是:
Why the type(st) returns " type 'type' "? (I thought it is instance not a class) Why the type(sc) returns " class 'main.ModelCreator' "?
How to Nonify the value of "name" field in the "Student" class, so it will only be saved in "st" (cause it is now somehow contained even in "sc")?
此代码有点复杂 - 但它所做的只是您告诉它要做的事情。
除了所需的描述符(即:包含 __get__
和 __set__
方法的 class 和通常的元 class 机制外,它还做了什么class 的 __new__
方法在几个方面是错误的。
首先,分配给 class __new__
的 new
方法通过使用对 type.
的硬编码调用结束其执行 - 这是最错误的最重要的是 - 作为类型 returns 一个新的 class - 不是一个实例。插入的 new
方法末尾的调用应该是 object.__new__
- 或者更好的是,使用一种机制在下一个 class 中调用 __new__
__mro__
(但这并不是微不足道的 - 因为您必须在围绕您插入的 new
方法的元 class __new__
代码中找到它)。
无论如何——如果你想让使用这个元class的class成为他们自己"class factories",那么在那里调用type
才有意义——那会return 全新的 class 不仅包含声明的字段,还包含发送的默认值。呼叫类型是您看到 type(st)
returning type
的原因 - 这是您的第一个问题。
然后,它仍然是错误的:在每个实例化时调用的 new
class 方法将默认属性设置为描述符(即 "field") - 而且默认值将应用于相同 class 的所有其他实例化 - 或从它继承的其他 classes。您应该在对 StringField
class 的调用中设置默认值(如果有的话),并在将成为 class 上的 __new__
的方法上设置值实例。
如果你先调用 superclass __new__
来获得一个实际的实例,然后循环传入的关键字参数,并使用 setattr
作为设置属性的机制。使用 setattr 将确保正确调用 StringField __set__
方法。
所以,这段代码中有很多奇怪的地方,但尝试通过重写你的元class __new__
来修复它,或多或少:
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)
也就是说,你真的不应该浪费你的时间去研究 Python 2.7 中的这种高级机制,此时 (2017) - Python 2 最后一个版本是在 2010 年,它将是2020 年停止维护 - 这些机制在 3.x 系列中得到了改进和改进。在 Python 3.6 中,有了 __set_name__
描述符功能和新的 __init_subclass__
机制,您甚至不需要为此处的预期结果使用自定义元 class。