这是类的定义:
class Child:
pass
class Parent:
child = Child()
其中Child
可以有不同的args
和kwargs
,因此请不要考虑这种表示法:child = Child(Parent, 'child')
完成作业之后:
result = Parent.child > 1
其中Parent
是一种类型(不是实例)
结果应该是:
{
'type': Parent,
'property': 'child',
'action': '>',
'value': 1,
}
我不确定它是否可能,但有没有人知道一招?
答案 0 :(得分:4)
是的,这是可能的,但您需要明确地将父引用存储在子对象中。 >
运算符可以与__gt__
method挂钩。
由于您正在生成包含child
属性的动态信息的输出,因此您可以使用__getattr__
method生成动态子对象,只要访问了其他不存在的属性,就会调用它:
class Child:
def __init__(self, parent_type, attr_name):
self._parent_type = parent_type
self._parent_attr_name = attr_name
def __gt__(self, other):
return {
'type': self._parent_type,
'property': self._parent_attr_name,
'action': '>',
'value': other,
}
class Parent:
def __getattr__(self, name):
return Child(type(self), name)
演示:
>>> Parent().child > 1
{'type': <class '__main__.Parent'>, 'property': 'child', 'action': '>', 'value': 1}
如果Parent
必须是一个类(为什么?),那么使用固定属性(child = Child(Parent, 'child')
),或使用元类:
class ParentMeta(type):
def __getattr__(cls, name):
return Child(cls, name)
class Parent(metaclass=ParentMeta):
pass
此时您可以获得相同的效果,但无需创建实例:
>>> Parent.child > 1
{'type': <class '__main__.Parent'>, 'property': 'child', 'action': '>', 'value': 1}
从Python 3.6开始,您还可以使用__set_name__
hook来捕获定义属性的类的类型:
class Child:
def __set_name__(self, owner, attr_name):
self._parent_type = owner
self._parent_attr_name = attr_name
def __gt__(self, other):
return {
'type': self._parent_type,
'property': self._parent_attr_name,
'action': '>',
'value': other,
}
请注意,这些属性未在__init__
中设置!您可以将子对象创建为类的属性,此时会自动调用__set_name__
方法。输出再次相同:
>>> class Parent:
... child = Child()
...
>>> Parent.child > 1
{'type': <class '__main__.Parent'>, 'property': 'child', 'action': '>', 'value': 1}
请注意,子类不会反映在父类型中;你必须每次使用__get__
method捕获当前类型并返回一个包装器对象:
class ChildWrapper:
def __init__(self, child, parent_type):
self._child = child
self._parent_type = parent_type
def __getattr__(self, name):
return getattr(self._child, name)
def __gt__(self, other):
return {
'type': self._parent_type,
'property': self._child._parent_attr_name,
'action': '>',
'value': other,
}
class Child:
def __set_name__(self, owner, attr_name):
self._parent_attr_name = attr_name
def __get__(self, instance, owner):
return ChildWrapper(self, owner)
这会在每个属性访问(在类或实例上)上创建一个ChildWrapper()
实例,并且所有进一步的属性访问都会被委托回原始Child()
实例(但请注意特殊方法{{ 3}}):
>>> class Parent:
... child = Child()
...
>>> Parent.child
<__main__.ChildWrapper object at 0x10fc564e0>
>>> Parent.child > 1
{'type': <class '__main__.Parent'>, 'property': 'child', 'action': '>', 'value': 1}
因为每次根据访问对象创建ChildWrapper
,所以这也适用于子类化,跟踪当前类型:
>>> class Subclass(Parent):
... pass
...
>>> Subclass.child > 1
{'type': <class '__main__.Subclass'>, 'property': 'child', 'action': '>', 'value': 1}
答案 1 :(得分:0)
供将来参考:
class BaseChild:
type = None
property = None
def __gt__(self, other):
return {
'type': self.type,
'property': self.property,
'action': '>',
'value': other,
}
class MetaParent(type):
def __new__(mcs, name, bases, attrs):
cls = super().__new__(mcs, name, bases, attrs)
# This is the needed trick
for name, attr in attrs.items():
if isinstance(attr, BaseChild):
attr.type = cls
attr.property = name
return cls
class SomeChild(BaseChild):
pass
class Parent(metaclass=MetaParent):
first_child = SomeChild()
print(Parent.first_child > 1)
结果:
{'type': <class '__main__.Parent'>, 'value': 1, 'property': 'first_child', 'action': '>'}