- android - 多次调用 OnPrimaryClipChangedListener
- android - 无法更新 RecyclerView 中的 TextView 字段
- android.database.CursorIndexOutOfBoundsException : Index 0 requested, 光标大小为 0
- android - 使用 AppCompat 时,我们是否需要明确指定其 UI 组件(Spinner、EditText)颜色
我有一个任务:
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.nameThe 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/
我有一个任务: Implement metaclass "ModelCreator", that allows to declare class fields in the following for
我是一名优秀的程序员,十分优秀!