【问题标题】:How can a class run some function when any of its attributes are modified?当一个类的任何属性被修改时,它如何运行某个函数?
【发布时间】:2020-03-26 10:08:20
【问题描述】:

当类的任何属性被修改时,是否有一些通用的方法可以让类运行函数?我想知道是否可以运行某些子进程来监视对类的更改,但也许有一种方法可以从 class 继承并修改一些作为 Python 类一部分的 on_change 函数,有点像默认的 @987654324可以修改类的@方法。这里有什么明智的方法?

实际的应用程序不仅仅是打印输出,而是更新数据库中与实例化类的数据属性相对应的条目。

#!/usr/bin/env python

class Event(object):
    def __init__(self):
        self.a = [10, 20, 30]
        self.b = 15
    #def _on_attribute_change(self):
    #    print(f'attribute \'{name_of_last_attribute_that_was_changed}\' changed')

event = Event()
event.a[1] = 25
# printout should happen here: attribute 'a' changed
event.a.append(35)
# printout should happen here: attribute 'a' changed
event.c = 'test'
# printout should happen here: attribute 'c' changed

【问题讨论】:

标签: python class attributes


【解决方案1】:

您可以覆盖__setattr__ 魔术方法。

class Foo:

    def on_change(self):
        print("changed")

    def __setattr__(self, name, value):
        self.__dict__[name] = value
        self.on_change()

【讨论】:

  • 非常感谢您提出的解决方案。我正在寻找更通用的东西,比如涵盖经历附加操作或其他东西的数据属性的东西,并且在那里选择的解决方案似乎具有该功能。
【解决方案2】:

您可以覆盖__setattr__

class Event:
    def __init__(self):
        self.a = [10, 20, 30]
        self.b = 15

    def __setattr__(self, attr, value):
        print(f'attribute {attr} changed')
        super().__setattr__(attr, value)

但是,这仅检测 直接 到属性的分配。 event.a[1] = 25 是对event.a.__setitem__(1, 25) 的调用,所以Event 对此一无所知;它由event.a 解析为的任何值完全处理。

如果您不希望 __init__ 中的分配触发通知,请直接调用 super().__setattr__ 以避免调用您的覆盖。

def __init__(self):
    super().__setattr__('a', [10, 20, 30])
    super().__setattr(__('b', 15)

【讨论】:

  • 非常感谢您提供的代码。它几乎涵盖了我需要做的所有事情,但它不包括需要执行诸如附加到数据属性之类的事情。我正在研究所选解决方案建议的方法。再次感谢您的解决方案。
【解决方案3】:

最近我在 python 上开发了服务器端,我不得不检测列表/字典/任何你想要的变化,拯救我生命的库是 traits。我强烈推荐它。您可以轻松检查更改/删除/添加到您的属性的内容。

你可以阅读更多here

特别针对您的情况,notification 章节是最相关的

这是我刚跑的一个小sn-p:

from traits.api import *


class DataHandler(HasTraits):
    a = List([10, 20, 30])
    b = Int(15)


class Event(HasTraits):
    def __init__(self):
        super().__init__()
        self.data_handler = DataHandler()
        self.data_handler.on_trait_change(Event._anytrait_changed)

    @staticmethod
    def _anytrait_changed(obj, name, old, new):
        is_list = name.endswith('_items')
        if is_list:
            name = name[0:name.rindex('_items')]
        current_val = getattr(obj, name)
        if is_list:
            # new handles all the events(removed/changed/added to the list)
            if any(new.added):
                print("{} added to {} which is now {}".format(new.added, name, current_val))
            if any(new.removed):
                print("{} removed from {} which is now {}".format(new.removed, name, current_val))
        else:
            print('The {} trait changed from {} to {} '.format(name, old, (getattr(obj, name))))

e = Event()
e.data_handler.b = 13
e.data_handler.a.append(15)
e.data_handler.a.remove(15)
e.data_handler.a.remove(20)

输出:

The b trait changed from 15 to 13 
[15] added to a which is now [10, 20, 30, 15]
[15] removed from a which is now [10, 20, 30]
[20] removed from a which is now [10, 30]

希望这会有所帮助。

【讨论】:

    猜你喜欢
    • 2019-04-12
    • 1970-01-01
    • 2018-10-10
    • 2023-02-04
    • 1970-01-01
    • 2011-06-16
    • 1970-01-01
    • 2011-10-30
    • 1970-01-01
    相关资源
    最近更新 更多