在函数调用之间保存数据的Pythonic方式是什么?

问题描述 投票:0回答:4

对我来说,上下文是我需要在调用修改该值的函数之间保留的单个 int 的信息。我可以使用全局,但我知道这是不鼓励的。现在,我使用了包含 int 的列表形式的默认参数,并利用了可变性,以便在调用之间保留对值的更改,就像这样 -

 def increment(val, saved=[0]):
    saved[0] += val
    # do stuff

此功能通过 tkinter 附加到按钮,就像这样〜

button0 = Button(root, text="demo", command=lambda: increment(val))

这意味着我没有可以分配给函数外部的局部变量的返回值。

人们通常如何处理这个问题?我的意思是,当然,可变性技巧确实有效,但是如果我需要从多个函数访问和修改该值怎么办?

如果不设置一个具有静态方法和内部属性等的类,这难道就不能完成吗?

python variables state
4个回答
6
投票

使用一个类。 使用实例成员来保持状态。

class Incrementable:
    def __init__(self, initial_value = 0):
        self.x = initial_value
    def increment(self, val):
        self.x += val
        # do stuff

您可以添加

__call__
方法来模拟函数调用(例如,如果您需要向后兼容)。 这是否是一个好主意实际上取决于上下文和您的具体用例。

如果不设置一个具有静态方法和内部属性等的类,这难道就不能完成吗?

它可以,但是不涉及具有属性的类/对象的解决方案不是“Pythonic”。 在 python 中定义类非常容易(上面的示例只有 5 行简单代码),并且它为您提供了最大的控制和灵活性。

使用Python的mutable-default-args“怪异”(我不会称其为“功能”)应该被视为a hack


1
投票

如果您不想设置类,那么您唯一的1其他选择是全局变量。您无法将其保存到局部变量,因为该命令从

mainloop
内运行,而不是在创建它的本地范围内运行。

例如:

button0 = Button(root, text="demo", command=lambda: increment_and_save(val))

def increment_and_save(val):
    global saved
    saved = increment(val)

1 从字面上看并不正确,因为您可以使用各种其他方式来保存数据,例如数据库或文件,但我假设您需要内存中的解决方案。


1
投票

你是不是混淆了模型和视图?

UI 元素(例如按钮)应该仅委托给您的数据模型。因此,如果您有一个具有持久状态的模型(即具有属性的类),您可以在那里实现一个类方法,该方法在单击按钮时处理所需的事情。

如果您尝试将有状态的事物绑定到您的演示文稿 (UI),您将因此失去所述演示文稿和数据模型之间的理想分离。

如果您想让数据模型访问保持简单,您可以考虑一个

singleton
实例,这样您就不需要将该模型的引用作为所有 UI 元素的参数(而且您也不需要需要一个全局实例,即使这个
singleton
拥有某种全局可用的实例):

def singleton(cls):
    instance = cls()
    instance.__call__ = lambda: instance
    return instance

@singleton
class TheDataModel(object):
    def __init__(self):
        self.x = 0

    def on_button_demo(self):
        self.x += 1

if __name__ == '__main__':
    # If an element needs a reference to the model, just get
    # the current instance from the decorated singleton:
    model = TheDataModel
    print('model', model.x)
    model.on_button_demo()
    print('model', model.x)

    # In fact, it is a global instance that is available via
    # the class name; even across imports in the same session
    other = TheDataModel
    print('other', other.x)

    # Consequently, you can easily bind the model's methods
    # to the action of any UI element
    button0 = Button(root, text="demo", command=TheDataModel.on_button_demo)

但是,我必须指出这一点,在使用

singleton
实例时要小心,因为它们很容易导致糟糕的设计。设置适当的模型,然后以
singleton
的形式访问主要模型化合物。这种统一访问通常被称为上下文


0
投票

我们可以通过使用

context managers
使其面向上下文。示例并非特定于 UI 元素,而是说明一般场景。

class MyContext(object):
    # This is my container 
    # have whatever state to it
    # support different operations
    
    def __init__(self):
        self.val = 0
        
    def increment(self, val):
        self.val += val
        
    def get(self):
        return self.val
        
    def __enter__(self):
        # do on creation
        return self
        
        
    def __exit__(self, type, value, traceback):
        # do on exit
        self.val = 0
        
def some_func(val, context=None):
    if context:
        context.increment(val)
        
def some_more(val, context=None):
    if context:
        context.increment(val)
        
def some_getter(context=None):
    if context:
        print context.get()
       
with MyContext() as context:
    some_func(5, context=context)
    some_more(10, context=context)
    some_getter(context=context)
© www.soinside.com 2019 - 2024. All rights reserved.