【问题标题】:How to add attribute to arbitrary object python?如何将属性添加到任意对象python?
【发布时间】:2021-10-26 03:33:50
【问题描述】:

对于我正在进行的项目,我希望能够将名称与对象相关联。我想这样做的方法是将对象的 .name 属性设置为我想要的名称。我真正需要的是一个函数,它接受一个对象的实例,并返回在各方面都相同但具有 .name 属性的东西。问题是我不知道对象将是什么类型的数据,所以我不能使用子类化例如

我尝试过的每种方法都遇到了问题。试图直接给它一个 .name 属性是行不通的,例如:

>>> cats = ['tabby', 'siamese']
>>> cats.name = 'cats'
Traceback (most recent call last):
  File "<pyshell#197>", line 1, in <module>
    cats.name = 'cats'
AttributeError: 'list' object has no attribute 'name'

使用 setattr 也有同样的问题。

我尝试创建一个新类,该类在 init 上复制实例中的所有属性,并且还有一个 .name 属性,但这也不起作用。如果我尝试:

class NamedThing:
  def __init__(self, name, thing):
    thing_dict = {#not all types have a .__dict__ method
      name: getattr(thing, name) for name in dir(thing)
    }
    self.__dict__ = thing_dict
    self.name = name

它复制 dict 没有问题,但由于某种原因,除非我直接调用新方法,否则 python 无法找到它们,因此该对象失去了所有功能。例如:

>>> cats = ['tabby', 'siamese']
>>> named_thing_cats = NamedThing('cats', cats)

>>> named_thing_cats.__repr__()#directly calling .__repr__()
"['tabby', 'siamese']"
>>> repr(named_thing_cats)#for some reason python does not call the new repr method
'<__main__.NamedThing object at 0x0000022814C1A670>'

>>> hasattr(named_thing_cats, '__iter__')
True
>>> for cat in named_thing_cats:
    print(cat)
Traceback (most recent call last):
  File "<pyshell#215>", line 1, in <module>
    for cat in named_thing_cats:
TypeError: 'NamedThing' object is not iterable

我也试过直接设置class来设置类型和属性:

class NamedThing:
  def __init__(self, name, thing):
    thing_dict = {#not all types have a .__dict__ method
      name: getattr(thing, name) for name in dir(thing)
    }
    self.__class__ = type('NamedThing', (type(thing),), thing_dict)
    self.name = name

但这会遇到问题,具体取决于事物的类型:

>>> cats = ['tabby', 'siamese']
>>> named_thing_cats = NamedThing('cats', cats)
Traceback (most recent call last):
  File "<pyshell#217>", line 1, in <module>
    named_thing_cats = NamedThing('cats', cats)
  File "C:/Users/61490/Documents/Python/HeirachicalDict/moduleanalyser.py", line 12, in __init__
    self.__class__ = type('NamedThing', (type(thing),), thing_dict)
TypeError: __class__ assignment: 'NamedThing' object layout differs from 'NamedThing'

我真的被困住了,帮助会很棒

【问题讨论】:

    标签: python properties attributes


    【解决方案1】:

    这行得通吗?

    class Thingy(list):
        def __init__(self, name, thing):
            list.__init__(self, thing)
            self.name = name
    
    cats = Thingy('cats', ['tabby', 'siamese'])
    
    print(cats.name) # shows 'cats'
    
    for cat in cats:
        print(cat) # shows tabby, siamese
    

    或者你可以这样做:

    class Thingy:
        def __init__(self, name, thing):
            self.thing = thing
            self.name = name
    

    【讨论】:

    • 这对列表很有用,问题是我事先不知道数据是什么类型。它可以是一个类,也可以是一个完整的模块。可悲的是,并非所有类型都可以使用 super().__init__(self, thing)。这就是为什么我试图只复制 dict 的原因,但我不知道为什么这不起作用
    • @MatthewB 我更新了我的答案。您可以将任何数据类型存储在 self.thing 中的类 Thingy 中。
    • 我将它用作一个更大项目的一部分,我希望新对象直接保留原始对象的所有功能。如果我只需要将对象与名称链接,我可以使用带有 (name, object) 的元组或字典。
    【解决方案2】:

    为什么不直接用yield from 创建一个__iter__ 魔术方法:

    class NamedThing():
        def __init__(self, name, thing):
            self.thing = thing
            self.name = name
        def __iter__(self):
            yield from self.thing
    
    cats = ['tabby', 'siamese']
    named_thing_cats = NamedThing('cats', cats)
    for cat in named_thing_cats:
        print(cat)
    

    输出;

    tabby
    siamese
    

    【讨论】:

    • 这对列表很有用,问题是我事先不知道对象是什么类型。它可以是一个类,也可以是一个完整的模块。我需要从我给它的任何对象复制整个功能的东西,以及添加一个 .name
    【解决方案3】:

    您想要的称为对象代理。这是一些非常复杂的东西,因为您正在进入 python 的数据模型并以有趣的方式操作一些非常基本的 dunder(双下划线)方法

    class Proxy:
    
        def __init__(self, proxied):
            object.__setattr__(self, '_proxied', proxied)
    
        def __getattribute__(self, name):
            try:
                return object.__getattribute__(self, name)
            except AttributeError:
                p = object.__getattribute__(self, '_proxied')
                return getattr(p, name)
            
        def __setattr__(self, name, value):
            p = object.__getattribute__(self, '_proxied')
            if hasattr(p, name):
                setattr(p, name, value)
            else:
                setattr(self, name, value)
            
        def __getitem__(self, key):
            p = object.__getattribute__(self, '_proxied')
            return p[key]
            
        def __setitem__(self, key, value):
            p = object.__getattribute__(self, '_proxied')
            p[key] = value
            
        def __delitem__(self, key):
            p = object.__getattribute__(self, '_proxied')
            del p[key]
    

    这里发生的最明显的事情是,这个类在内部必须使用 dunders 的object 实现以避免无限递归。它的作用是保存对代理对象的引用,然后如果您尝试获取或设置一个属性,它将检查代理对象,如果代理对象具有该属性,它会使用它,否则它会在自身上设置该属性。对于索引,就像使用列表一样,它只是直接作用于代理对象,因为代理本身不允许索引。

    如果您需要在生产中使用它,有一个名为 wrapt 的包,您可能应该看看。

    【讨论】:

      猜你喜欢
      • 2015-11-15
      • 1970-01-01
      • 1970-01-01
      • 2011-11-23
      • 2014-09-18
      • 2016-08-29
      • 1970-01-01
      • 2011-05-14
      • 2015-09-21
      相关资源
      最近更新 更多