使用 __getattr__() 和 __setattr__() 提供多种访问 child 变量的方法

Using __getattr__() and __setattr__() to provide multiple ways to access child variables

尊敬的同事们!

请考虑...

#!/usr/bin/env python3.6

class Child(object):
  def __init__(self, name:str, value = 0):
    self.name = name;
    self.value = value;
  def __repr__(self):
    return '  child "{}" has value {}\n'.format(self.name, self.value)

class Parent(object):
  def __init__(self, name:str):
    self.name = name
    self.children = {}

  def __repr__(self):
    s = 'Parent "{}":\n'.format(self.name)
    for k, v in self.children.items():
      s += v.__repr__()
    return s

  def __getattr__(self, name:str):
    if name == 'children':
      return self.children;
    elif name in self.children.keys():
      return self.children[name].value
    else:
      return super().__getattr__(name)

  def __setattr__(self, prop, val):
    if prop == 'name':
      super().__setattr__(prop, val)
    else:
      super().__setattr__('children[{}]'.format(prop), val)


p = Parent('Tango')
p.children['Alfa'] = Child('Alfa', 55)
p.children['Bravo'] = Child('Bravo', 66)
print(p)
print(p.children['Alfa'])  # Returns '55' (normal)
print(p.Alfa)              # Returns '55' (__getattr__)

print('-----')
p.Alfa = 99    # This is creating a new variable, need __setattr__ ... 
print(p.Alfa)  # Prints new variable. __setattr__ is not called!
print('-----')

print(p.children['Alfa'])  # Still '55'
print(p)                   # Still '55'

代码的目的是让持有 Parent 的人可以通过两种方式访问​​其 childrenp.children['Alfa']p.Alfa

使用__getattr__()我可以完成这个read-side。 (注释掉上面代码中的 def __setattr__(),您可以看到 read-side 按预期工作。)输出 without setattr() 是:

Parent "Tango":
  child "Alfa" has value 55
  child "Bravo" has value 66

  child "Alfa" has value 55

55
-----
99
-----
  child "Alfa" has value 55

Parent "Tango":
  child "Alfa" has value 55
  child "Bravo" has value 66

当然现在我需要__setattr__()来完成这个write-side。我想将 99 分配给阿尔法。到现在还没想出避免各种问题的咒语

上面的代码 with setattr() 引发 RecursionError:

Traceback (most recent call last):
  File "./test.py", line 37, in <module>
    p.children['Alfa'] = Child('Alfa', 55)
  File "./test.py", line 23, in __getattr__
    return self.children;
  File "./test.py", line 23, in __getattr__
    return self.children;
  File "./test.py", line 23, in __getattr__
    return self.children;
  [Previous line repeated 328 more times]
  File "./test.py", line 22, in __getattr__
    if name == 'children':
RecursionError: maximum recursion depth exceeded in comparison

我希望后面的 测试代码 表明通过写入 p.Alfa,我实际上是在更新 self.children['Alfa'] 的值。赋值后打印出来应该是99,不是原来的55.

请注意,在现实世界中,可能的 children、它们的名称和内容几乎是无限的。

感谢您的帮助和见解!

def __setattr__(self, attr, val):
    if attr == 'name':
        super().__setattr__(self, 'name', val)
        return

    self.children[attr] = val

您的代码从不初始化名为 children 的属性。它初始化一个名为 children[children] 的对象。因此,当您稍后尝试分配给 self.children['Alfa'] 时,它尝试做的第一件事就是找到一个名为 children 的属性。当找不到时,它调用__getattr__,它说当name == "children"时,它应该return self.children,然后无限循环开始。

__getattr__ 在通过正常过程找不到属性时调用。正确定义 __setattr__ 后,永远不应为 children 调用 __getattr__,因为您在 __init__ 中定义了它。定义可以简化为

def __getattr__(self, name:str):
    if name in self.children.keys():
        return self.children[name].value
    else:
        return super().__getattr__(name)