模块的__setattr__和__getattr__在访问全局变量时
__getattr__
和 __setattr__
可以很好地处理通过点号来获取或设置对象的属性。那么,我该如何在模块内部拦截对全局变量的获取和设置呢?
我创建了一个模块类型的子类:
from types import ModuleType
class WUserModule(ModuleType):
def __init__(self, name):
super().__init__(name)
def __setattr__(self, name, value):
if name in self.__dict__:
super().__setattr__(name, value)
return
print('Set name {}, value {}'.format(name, str(value)))
def __getattr__(self, name):
print('Get name {}'.format(name))
接着创建一个空模块并加载代码:
module = WUserModule('form_module')
with open('user_module.py', 'r', encoding='utf8') as f:
exec(f.read(), module.__dict__)
从 user_module.py
加载的代码:
a=1
print(a)
我需要以某种方式拦截对变量 a
的访问。在加载的代码中,预期会访问一些在模块的 globals()
中不存在的变量,而我想要替换这些请求的值。
更新:
上面的代码并没有按我需要的那样工作:对变量 a
的访问没有在 print
中反映出来。
我正在使用 PyQt4 来编写一个“平台”,让其他用户(程序员)可以添加表单和模块来处理与这些表单的交互。用户模块中的表单通过“注入”的变量 form
进行访问。我想给用户提供一种更简单的方式来访问表单控件的值。比如,不用写 if form.myCheckbox.isChecked()
或 form.myCheckbox.setChecked(myValue)
,我想提供一个快捷方式:if myCheckbox
和 myCheckbox = myValue
,这样可以在后台拦截值的访问并完成必要的工作:
def __getattr__(self, name):
formWidget = getatttr(form, name)
if isinstance(formWidget, QLineEdit):
formWidget.setText(value)
...
3 个回答
这个可以用:
assignments = {}
# extending dict class
class WUserModule(dict):
def __init__(self, dict):
self.__dict__ = globals() | dict # to support of import statements inside exec
def __setitem__(self, key, value):
self.__dict__[key] = value
print(f"new assinment {key} = {value}")
assignments[key] = value
def __getitem__(self, key): # without this method doesn't work - don't know why ;)
return self.__dict__[key]
# initial values if needed
input_vars_dict={"a":0}
module = WUserModule(input_vars_dict)
with open('user_module.py', 'r', encoding='utf8') as f:
exec(f.read(), module)
# assignments filled with all assignment that happpened
print(assignments)
用户空间模块:
print("user_module.py")
print(a)
a=1
b=2
输出结果:
user_module.py
0
new assinment a = 1
new assinment b = 2
{'a': 1, 'b': 2}
你想做的事情在一般情况下是行不通的。一个属性不能同时有一个值和另一个属性。你可以选择用 form.myCheckbox = True
和 bool(form.myCheckbox)
,或者 用 form.myCheckbox.setChecked(True)
和 bool(form.myCheckbox.isChecked())
,但不能同时这样做。也就是说,form.myCheckbox
不能同时是 True
和一个 QCheckbox
对象。对于 bool
、int
和 unicode
这些类型可以处理,但会比较麻烦,而其他类型通常是没办法的。
另外,你对 __getattr__
的作用有些混淆。它不会被调用来获取已经存在的属性,__getattribute__
才会被调用。不过,使用 __getattribute__
是比较复杂的,你可能想把你的复选框做成描述符,或者使用属性,但那样的话你就得放弃模块,改用类。
附注:为什么 form 是一个模块,而不是某个对象的实例呢?
你不能这样做。
根本的问题是,属性重载只有在你访问某个属性的时候才有效,具体来说就是:
expr . ident
没有点(.),就无法进行属性重载。因此,无论你怎么做,像这样的序列
a
永远无法调用属性重载,不管其他什么东西可能等于值的a
。