【问题标题】:How to catch a particular name assignment?如何捕捉特定的名称分配?
【发布时间】:2018-07-21 04:50:48
【问题描述】:

(基于this question):

当对象的attribute 被设置时,可以覆盖对象的__setattr__ 魔术方法以获得额外的指令。如:

class MyClass(object):
    def __init__(self, attribute=None):
        object.__init__(self)
        self.attribute = attribute


    def __setattr__(self, name, value):
        self.__dict__[name] = value
        if name == 'attribute':
            print("attribute's value is modified to {}.".format(
                                                        self.attribute))


if __name__ == '__main__':
    my_obj = MyClass(True)
    while True:
        my_obj.attribute = input()
  • 如何在当前脚本中捕获特定的名称分配 不使用类(特别是调用具有更多 说明)?

 

def b_is_modified():
    print("b is modified!")


if __name__ == '__main__':
    a = 3
    b = 4
    b = 5

b被赋值时如何调用b_is_modified

【问题讨论】:

    标签: python variable-assignment assign


    【解决方案1】:

    基于this answer:

    无法被捕获(至少在 python 级别)。

    简单的名称赋值(b = 4),与对象属性赋值(object.b = 5)相反,是语言本身的基本操作。它不是根据可以覆盖的较低级别的操作来实现的。赋值只是

    【讨论】:

      【解决方案2】:

      我认为 Nae 的另一个答案总结了这一点;我不知道 Python 语言中有任何内置机制来检测分配,所以如果你想要一个类似中断的事件系统在分配时触发,我不知道它是否可行。

      但是,您似乎很安静地决心要找到一种“检测”分配的方法,所以我想描述一种可能让您更接近的方法。

      有内置函数globals()locals() 分别在全局和局部范围内创建变量字典。 (除了vars() 之外,还进一步解释了here)。

      值得注意的一点是,如果从函数内部调用locals(),其行为会有所不同:

      如果locals()inside 一个函数中被调用,它会在那个时刻构造函数命名空间的字典并返回它——任何进一步的名称分配都反映在返回的字典中,并且任何字典的分配反映在实际的本地命名空间中

      如果locals()外部 一个函数被调用,它会返回作为当前命名空间的实际字典。对命名空间的进一步更改反映在字典中,对字典的更改反映在命名空间中:

      这是一种检测变量变化的“hacky”方法:

      def b_is_modified():
          print("b is modified!")
      
      
      if __name__ == '__main__':
          old = locals().get('b')
      
          a = 3
          b = 4
          b = 5
      
          new = locals().get('b')
          if id(new) != id(old) and new is not None:
              b_is_modified()
      

      这不过是一种(混淆的?)检查b 的值是否已从执行点更改为另一点的方法,并且没有检测到它的回调事件或触发操作。但是,如果您想扩展此方法,请继续阅读。

      答案的其余部分解释了如何通过将b 重写为以下内容来检查更改:

      if __name__ == '__main__':
          monitor = ScopeVariableMonitor(locals())
      
          a = 3
          b = 4
          monitor.compare_and_update()  # Detects creation of a and b
          b = 5
          monitor.compare_and_update()  # Detects changes to b
      

      以下内容将“检测”变量的任何更改,我还提供了一个在函数内部使用它的示例,以重申从 locals() 返回的字典不会更新。

      ScopeVariableMonitor-class 只是一个示例,它将代码组合在一个地方。本质上,它是比较update()s 之间变量的存在和值的变化。

      class ScopeVariableMonitor:
          def __init__(self, scope_vars):
              self.scope_vars = scope_vars  # Save a locals()-dictionary instance
              self.old = self.scope_vars.copy()  # Make a shallow copy for later comparison
      
          def update(self, scope_vars=None):
              scope_vars = scope_vars or self.scope_vars
              self.old = scope_vars.copy()  # Make new shallow copy for next time
      
          def has_changed(self, var_name):
              old, new = self.old.get(var_name), self.scope_vars.get(var_name)
              print('{} has changed: {}'.format(var_name, id(old) != id(new)))
      
          def compare_and_update(self, var_list=None, scope_vars=None):
              scope_vars = scope_vars or self.scope_vars
              # Find new keys in the locals()-dictionary
              new_variables = set(scope_vars.keys()).difference(set(self.old.keys()))
              if var_list:
                  new_variables = [v for v in new_variables if v in var_list]
              if new_variables:
                  print('\nNew variables:')
                  for new_variable in new_variables:
                      print(' {} = {}'.format(new_variable, scope_vars[new_variable]))
      
              # Find changes of values in the locals()-dictionary (does not handle deleted vars)
              changed_variables = [var_name for (var_name, value) in self.old.items() if
                                   id(value) != id(scope_vars[var_name])]
              if var_list:
                  changed_variables = [v for v in changed_variables if v in var_list]
              if changed_variables:
                  print('\nChanged variables:')
                  for var in changed_variables:
                      print('  Before: {} = {}'.format(var, self.old[var]))
                      print(' Current: {} = {}\n'.format(var, scope_vars[var], self.old[var]))
              self.update()
      

      “有趣”的部分是compare_and_update()-方法,如果提供了变量名称列表,例如['a', 'b'],它只会查找对变量的更改。 scope_vars-参数在函数范围内时是必需的,但在全局范围内则不需要;原因如上所述。

      def some_function_scope():
          print('\n --- Now inside function scope --- \n')
          monitor = ScopeVariableMonitor(locals())
          a = 'foo'
          b = 42
          monitor.compare_and_update(['a', 'b'], scope_vars=locals())
          b = 'bar'
          monitor.compare_and_update(scope_vars=locals())
      
      
      if __name__ == '__main__':
          monitor = ScopeVariableMonitor(locals())
          var_list = ['a', 'b']
          a = 5
          b = 10
          c = 15
          monitor.compare_and_update(var_list=var_list)
      
          print('\n *** *** *** \n')  # Separator for print output
      
          a = 10
          b = 42
          c = 100
          d = 1000
          monitor.has_changed('b')
          monitor.compare_and_update()
      
          some_function_scope()
      

      输出:

      New variables:
       a = 5
       b = 10
      
       *** *** *** 
      
      b has changed: True
      
      New variables:
       d = 1000
      
      Changed variables:
        Before: b = 10
       Current: b = 42
      
        Before: a = 5
       Current: a = 10
      
        Before: c = 15
       Current: c = 100
      
      
       --- Now inside function scope --- 
      
      New variables:
       a = foo
       b = 42
      
      Changed variables:
        Before: b = 42
       Current: b = bar
      

      结论

      我的回答只是一种更一般的做法:

      b = 1
      old_b = b
      
      # ...
      
      if b != old_b:
          print('b has been assigned to')
      

      来自locals() 的字典将保存所有变量,包括函数和类;不仅仅是像 abc 这样的“简单”变量。

      在上面的实现中,“旧”和“新”值之间的检查是通过比较之前 shallow 副本的 id() 与当前值的 id() 来完成的。这种方法允许比较任何值,因为id() 将返回虚拟内存地址,但这远不是一个好的、通用的比较方案。

      我很好奇您想要实现什么以及为什么要检测分配:如果您分享您的目标,那么也许我可以想出另一种方式以另一种方式实现它。

      【讨论】:

      • 我想模仿 __setattr__ 可以为对象覆盖的方式,用于全局变量赋值,而不是 object.attribute 赋值。
      • 我明白了。你不能上课是有什么特殊原因,还是只是好奇?
      • 我只是好奇。我想为某事做一个例子,为了简单起见,我不想创建一个类。
      猜你喜欢
      • 2017-09-22
      • 2012-06-01
      • 2013-06-10
      • 1970-01-01
      • 2011-10-29
      • 1970-01-01
      • 1970-01-01
      • 2016-05-12
      • 1970-01-01
      相关资源
      最近更新 更多