我需要向python对象动态添加属性(包含元组或对象)。这适用于我编写的Python类,但不适用于内置类。
考虑以下程序:
import numpy as np
class My_Class():
pass
my_obj = My_Class()
my_obj2 = My_Class()
my_obj.__my_hidden_field = (1,1)
my_obj2.__my_hidden_field = (2,1)
print(my_obj.__my_hidden_field, my_obj2.__my_hidden_field)
这将正确打印(1, 1) (2, 1)
。但是,以下程序无效。
X = np.random.random(size=(2,3))
X.__my_hidden_field = (3,1)
setattr(X, '__my_hidden_field', (3,1))
以上两行均引发以下错误# AttributeError: 'numpy.ndarray' object has no attribute '__my_hidden_field'
现在,从这些问题(即Attribute assignment to built-in object,Can't set attributes of object class,python: dynamically adding attributes to a built-in class)中发现的原因是Python不允许将属性动态添加到内置对象。
答案摘录:https://stackoverflow.com/a/22103924/8413477
这是有意禁止的,以防止对内置类型进行意外的致命更改(对您从未使用过的部分代码致命)。另外,这样做是为了防止更改影响驻留在地址空间中的不同解释器,因为内置类型(不同于用户定义的类)在所有此类解释器之间共享。
但是,所有答案都很老,我非常需要在我的研究项目中这样做。
有一个模块可以通过以下方法将方法添加到内置的Class中: https://pypi.org/project/forbiddenfruit/
但是,它不允许向每个对象添加对象/属性。
有帮助吗?
答案 0 :(得分:0)
您可能想要weakref.WeakKeyDictionary
。在文档中,
这可用于将其他数据与应用程序其他部分拥有的对象相关联,而无需向这些对象添加属性。
与属性类似,它不同于普通的dict,它允许对象在没有其他引用时被垃圾回收。
您将使用
查找字段my_hidden_field[X]
代替
X._my_hidden_field
两个警告:首先,由于弱键可能会在没有警告的情况下随时删除,因此您不应该遍历WeakKeyDictionary
。查找对象可以参考。其次,您不能对用C编写的没有插槽的对象类型(对于许多内置插件为true)或用Python编写的不允许__weakref__
的对象类型进行弱引用。属性(通常归因于__slots__
)。
如果这是一个问题,您可以对这些类型使用常规的dict,但是您必须自己清理它。
答案 1 :(得分:0)
是否可以在Python中动态地向内置python对象中添加属性?
不,现在您在发布的链接中阅读的原因相同。但是我想出了一个食谱,我认为这可能是您的示踪剂的起点。
在阅读了很多有关此内容的内容之后,我提出了一个可能不是完整解决方案的食谱,但可以肯定,您可以从这里开始。
此菜谱的优点是它不使用第三方库,所有这些都是通过标准(Python 3.5、3.6、3.7)库实现的。
此食谱将对这样的代码进行检测(在这里执行简单的检测,这只是概念上的问题)并执行。
# target/target.py
d = {1: 2}
d.update({3: 4})
print(d) # Should print "{1: 2, 3: 4}"
print(d.hidden_field) # Should print "(0, 0)"
首先,我们必须在要添加的内容中添加hidden_field
(此食谱仅通过字典进行过测试)。
以下代码接收一个值,找出其类型/类并将其子类化,以添加所提到的hidden_field
。
def instrument_node(value):
VarType = type(value)
class AnalyserHelper(VarType):
def __init__(self, *args, **kwargs):
self.hidden_field = (0, 0)
super(AnalyserHelper, self).__init__(*args, **kwargs)
return AnalyserHelper(value)
有了这个,您就可以:
d = {1: 2}
d = instrument_node(d)
d.update({3: 4})
print(d) # Do print "{1: 2, 3: 4}"
print(d.hidden_field) # Do print "(0, 0)"
在这一点上,我们已经知道“向内置词典中添加工具”的方法 ,但是这里没有透明性。
下一步是“隐藏” instrument_node
调用,我们将使用ast Python模块进行此操作。
以下是AST节点转换器,它将使用它找到的任何词典并将其包装在instrument_node
调用中:
class AnalyserNodeTransformer(ast.NodeTransformer):
"""Wraps all dicts in a call to instrument_node()"""
def visit_Dict(self, node):
return ast.Call(func=ast.Name(id='instrument_node', ctx=ast.Load()),
args=[node], keywords=[])
return node
使用thats工具,您可以编写一个脚本,该脚本:
- 读取目标代码。
- 解析程序。
- 应用AST更改。
- 编译它。
- 执行它。
import ast
import os
from ast_transformer import AnalyserNodeTransformer
# instrument_node need to be in the namespace here.
from ast_transformer import instrument_node
if __name__ == "__main__":
target_path = os.path.join(os.path.dirname(__file__), 'target/target.py')
with open(target_path, 'r') as program:
# Read and parse the target script.
tree = ast.parse(program.read())
# Make transformations.
tree = AnalyserNodeTransformer().visit(tree)
# Fix locations.
ast.fix_missing_locations(tree)
# Compile and execute.
compiled = compile(tree, filename='target.py', mode='exec')
exec(compiled)
这将获取我们的目标代码,并用instrument_node()
包装每个字典并执行这种更改的结果。
针对我们的目标代码运行此命令的输出
# target/target.py
d = {1: 2}
d.update({3: 4})
print(d) # Will print "{1: 2, 3: 4}"
print(d.hidden_field) # Will print "(0, 0)"
是:
>>> {1: 2, 3: 4}
>>> (0, 0)
您可以克隆一个有效的示例here。
答案 2 :(得分:-2)
是的,这可能是python最酷的东西之一,在Python中,所有类都是由type
类
您可以详细阅读here,但这是您要做的
In [58]: My_Class = type("My_Class", (My_Class,), {"__my_hidden_field__": X})
In [59]: My_Class.__my_hidden_field__
Out[59]:
array([[0.73998002, 0.68213825, 0.41621582],
[0.05936479, 0.14348496, 0.61119082]])
*由于缺少继承而进行了编辑,因此您需要将原始类作为第二个参数(以元组形式)传递,以便其进行更新,否则它将仅重新编写该类)