我希望能够在具有子节点和孙子节点的节点实例上链接方法。当我在该顶级节点上调用方法时,我希望能够返回该对象的子孙。我还希望能够从最顶层的节点获得这些子孙的属性。我有以下数据模型:
class GrandParent(object):
def __init__(self,name='',age=0,is_retired=True,children=[]):
self.name = name
self.age = age
self.is_retired = is_retired
if children:
self.children = children
def print_mychildren(self):
for child in self.children:
print "Child: ",child
def mychildren(self):
return self.children
def __str__(self):
return "name: %s age: %s is_retired:%s" %(self.name,self.age,self.is_retired)
class Parent(object):
def __init__(self,name='',age=0,has_mortgage=True,children=[]):
self.name = name
self.age = age
self.has_mortgage = has_mortgage
if children:
self.children = children
def print_mychildren(self):
for child in self.children:
print "Child: ",child
def __str__(self):
return "name: %s age: %s has_mortgage: %s" %(self.name,self.age,self.has_mortgage)
class Child(object):
def __init__(self,name='',age=0,has_toys=True):
self.name = name
self.age = age
self.has_toys = has_toys
def __str__(self):
return "name: %s age: %s has_toys:%s" %(self.name,self.age,self.has_toys)
if __name__ == '__main__':
beaver = Child('Beaver',12,True)
ward = Child('Ward',16,False)
june = Parent('June',38,True,[beaver,ward])
grandpa = GrandParent('Grandpa',61,True,[june])
print grandpa
grandpa.print_mychildren() # print June
# Doesn't work
grandpa.mychildren().print_mychildren() # I want it to print Ward and Beaver
# Doesn't work
print grandpa.mychildren().mychild('Beaver').age # I want it to return an age
请注意,我希望将GrandParent,Parent和Child类分开,因为我想为每个类提供不同的属性,例如has_mortgage或is_retired。
从上面的数据模型中,我希望能够链接方法以遍历顶级节点的子节点。它看起来像这样:
grandpa.children # returns a list of children
print grandpa.mychild('June').has_mortgage # returns the value
grandpa.mychildren().mychildren() # prints a list of grandchildren
print grandpa.mychildren().mychild('Ward').has_toys # return the value
换句话说,我可以发表声明:
print grandpa.mychildren().mychildren()
表现得像:
for child in grandpa.children:
for grandchild in child.children:
print grandchild
感谢您对此的回答。谢谢。
保罗
伊利诺伊州芝加哥
答案 0 :(得分:2)
你可以做你想做的事,但这需要一些工作。您需要将children
和Parent
类的Grandparent
值设置为将方法和成员变量的访问映射到其内容的自定义容器。
这是一个可能的实现。它可能有一些我没有解决的奇怪的角落情况,但它适用于我尝试过的基本内容:
from operator import attrgetter
class mappingContainer(object):
def __init__(self, contents):
self.contents = contents
# sequence protocol methods
def __getitem__(self, index):
return self.contents[index]
def __setitem__(self, index, value):
self.contents[index] = value
def __iter__(self):
return iter(self.contents)
def __contains__(self, o):
return o in self.contents
def __len__(self):
return len(self.contents)
# map attribute access and method calls
def __getattr__(self, name):
return mappingContainer(map(attrgetter(name), self.contents))
def __call__(self, *args, **kwargs):
return mappingContainer([o(*args, **kwargs) for o in self.contents])
# string conversions
def __repr__(self):
return "mappingContainer(%s)" % repr(self.contents)
这是一个简单的“人”类,我曾用它来测试它。每个人都有mappingContainer
中的一个名字,零个或多个孩子,以及一个任意命名的方法,可以打印一些东西并返回一个值。
class Person(object):
def __init__(self, name, children = None):
self.name = name
if not children:
children = []
self.children = mappingContainer(children)
def __repr__(self):
return self.name
def someMethod(self):
print "%s's someMethod()" % str(self)
return "%s's return value" % str(self)
以下是我测试它的方法(长线包裹,显示在这里):
>>> c1 = Person("Adam")
>>> c2 = Person("Albert")
>>> c3 = Person("Alice")
>>> c4 = Person("Agnes")
>>> p1 = Person("Bob", [c1, c2])
>>> p2 = Person("Betty", [c3,c4])
>>> gp = Person("Charles", [p1, p2])
>>> gp
Charles
>>> gp.children
mappingContainer([Bob, Betty])
>>> gp.children.children
mappingContainer([mappingContainer([Adam, Albert]),
mappingContainer([Alice, Agnes])])
>>> gp.children.children.someMethod()
Adam's someMethod()
Albert's someMethod()
Alice's someMethod()
Agnes's someMethod()
mappingContainer([mappingContainer(["Adam's return value",
"Albert's return value"]),
mappingContainer(["Alice's return value",
"Agnes's return value"])])
现在这可能不是你想要的,因为你不能通过gp.children.children
直接迭代孙子(虽然函数调用会一直向下冒泡,但它们的结果仍然会嵌套在两层中容器)。我认为mappingContainer可能会以某种方式进行调整以解决这个问题,但我不确定它是否值得。
如果出现任何问题,这种类型的链接调用将成为调试的噩梦(除非你是超人,否则你的代码中的某些内容在开发的某些时候总会出错)。如果您明确地迭代或递归您的关系层次结构,您的代码将更容易编写和理解。
答案 1 :(得分:1)
一种方法是为列表集合创建一个包装器(与jQuery处理方法链接的方式略有相似)
代码:
class ChainList(list):
def __getattribute__(self, name):
try:
return object.__getattribute__(self, name)
except:
pass
return ChainList([getattr(child, name)
for child in self if name in dir(child)])
def __call__(self, *args, **kwargs):
return ChainList([child(*args, **kwargs)
for child in self if callable(child)])
class GrandParent(object):
def __init__(self,name='',age=0,is_retired=True,children=[]):
self.name = name
self.age = age
self.is_retired = is_retired
if children:
self.children = ChainList(children)
def print_mychildren(self):
for child in self.children:
print "Child: ",child
def mychildren(self):
return self.children
def __str__(self):
return "name: %s age: %s is_retired:%s" %(self.name,self.age,self.is_retired)
class Parent(object):
def __init__(self,name='',age=0,has_mortgage=True,children=[]):
self.name = name
self.age = age
self.has_mortgage = has_mortgage
if children:
self.children = ChainList(children)
def print_mychildren(self):
for child in self.children:
print "Child: ",child
def __str__(self):
return "name: %s age: %s has_mortgage: %s" %(self.name,self.age,self.has_mortgage)
def mychild(self, name):
for child in self.children:
if child.name == name:
return child
class Child(object):
def __init__(self,name='',age=0,has_toys=True):
self.name = name
self.age = age
self.has_toys = has_toys
def __str__(self):
return "name: %s age: %s has_toys:%s" %(self.name,self.age,self.has_toys)
if __name__ == '__main__':
beaver = Child('Beaver',12,True)
ward = Child('Ward',16,False)
june = Parent('June',38,True,[beaver,ward])
grandpa = GrandParent('Grandpa',61,True,[june])
print grandpa
grandpa.print_mychildren() # print June
# Doesn't work
grandpa.mychildren().print_mychildren() # I want it to print Ward and Beaver
# Doesn't work
print grandpa.mychildren().mychild('Beaver').age # I want it to return an age
主要思想是ChainList类,并覆盖__getattribute__
和__call__
并包装需要与此类链接的所有列表。
我还将mychild()添加到Parent以使示例正常工作。
答案 2 :(得分:0)
对我而言,这听起来非常像是在谈论模型和模型关系。看看Django。 Django Models将完全符合您的要求,您可以定义模型与query across those relations之间的关系。