python - 如何将属性添加到任意对象python?
问题描述
对于我正在进行的项目,我希望能够将名称与对象相关联。我想这样做的方法是将对象的 .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 有同样的问题。
我尝试创建一个新类,该类在初始化时复制实例中的所有属性并且还具有 .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 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 的数据模型并以有趣的方式操作一些非常基本的 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]
这里发生的最明显的事情是,在内部这个类必须使用object
dunders 的实现来避免无限递归。它的作用是保存对代理对象的引用,然后如果您尝试获取或设置一个属性,它将检查代理对象,如果代理对象具有该属性,它会使用它,否则它会在自身上设置该属性。对于索引,就像使用列表一样,它只是直接作用于代理对象,因为代理本身不允许索引。
如果您需要在生产中使用它,那么您可能应该查看一个名为wrapt的包。
推荐阅读
- c# - SSIS 脚本任务使用通配符删除文件
- android - protobuf-javalite + kotlin 执行 org.jetbrains.kotlin.gradle.internal.KaptExecution 时发生故障
- google-colaboratory - 谷歌 Colab 专区
- azure - Azure 表存储:出于合规性目的捕获所有表活动
- python - for循环中的python调度程序
- swift - 用于 VR 的 RealityKit
- pyspark - 如何在 PySpark 中创建数据框列表或数据框字典?
- javascript - 如何导入包并以角度调用函数?
- python - 注销功能提供 RecursionError 错误
- microsoft-graph-api - Microsoft Graph API 站点搜索返回已删除的站点