gpt4 book ai didi

python - 实现特殊的元类。继承类中的 Nonify 字段

转载 作者:太空宇宙 更新时间:2023-11-04 02:49:54 25 4
gpt4 key购买 nike

我有一个任务:

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 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__ 方法的类)和通常的元类机制之外,它的作用是插入 __new__。类的方法在几个方面是错误的。

首先,new分配给类的方法 __new__通过使用对 type. 的硬编码调用结束其执行- 这是最错误的事情 - 因为 type 返回一个新类 - 而不是一个实例。末尾的调用new方法应该是object.__new__ - 或者更好的是,使用一种调用 __new__ 的机制在下一个类上 __mro__ (但这并不是微不足道的 - 因为您必须在元类 __new__ 代码中找到围绕您插入的 new 方法的代码)。

无论如何 - 只有调用 type 才有意义如果你希望使用这个元类的类本身就是“类工厂”——那将返回全新的类,不仅有声明的字段,还有发送的默认值。调用类型是您看到 type(st) 的原因返回 type - 这是你的第一个问题。

那么,还是错了:new在每个实例化时调用的类方法将默认属性设置为描述符(即“字段”)——并且该默认值将应用于同一类的所有其他实例化——或从它继承的其他类。您应该在调用 StringField 时设置默认值(如果有的话)类 - 以及将成为 __new__ 的方法在类上,设置实例的值。

如果您首先调用父类(super class) __new__ 就可以做到这一点获取实际实例,然后遍历传入的关键字参数,并使用 setattr作为设置属性的机制。使用 setattr 将确保 StringField __set__方法被正确调用。

所以,这段代码中有很多奇怪的地方,但是通过重写你的元类来尝试修复它 __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)

也就是说,此时(2017 年)你真的不应该浪费时间研究 Python 2.7 中的这种高级机制 - Python 2 最后一次发布是在 2010 年,它将在 2020 年停止维护 - 这些机制已经改进并且在 3.x 系列中变得更好。在 Python 3.6 中,使用 __set_name__描述符功能和新的 __init_subclass__机制,您甚至不需要为此处的预期结果使用自定义元类。

关于python - 实现特殊的元类。继承类中的 Nonify 字段,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44211433/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com