在函数调用之间保存数据的 pythonic 方式是什么?
What is the pythonic way of saving data between function calls?
对我来说,上下文是我需要在调用修改该值的函数之间保留的单个 int 值的信息。我可以使用全局变量,但我知道这是不鼓励的。现在,我使用了包含 int 的列表形式的默认参数,并利用了可变性,以便在调用之间保留对值的更改,就像这样--
def increment(val, saved=[0]):
saved[0] += val
# do stuff
这个函数正在通过 tkinter 附加到按钮上,像这样~
button0 = Button(root, text="demo", command=lambda: increment(val))
这意味着没有 return 值可以分配给函数外的局部变量。
人们通常如何处理这种情况?我的意思是,当然,可变性技巧有效,但是如果我需要从多个函数访问和修改该值怎么办?
如果不设置一个带有静态方法和内部属性等的 class 就不能做到这一点吗?
使用 class。使用实例成员来保持状态。
class Incrementable:
def __init__(self, initial_value = 0):
self.x = initial_value
def increment(self, val):
self.x += val
# do stuff
您可以添加一个 __call__
方法来模拟函数调用(例如,如果您需要 backward-compatible)。这是否是一个好主意实际上取决于上下文和您的具体用例。
Can this not be done without setting up a class with static methods and internal attributes, etc?
可以,但不涉及 classes/objects 属性的解决方案不是 "pythonic"。在 python 中定义 classes 非常容易(上面的例子只有 5 行简单的代码),它给了你最大的控制和灵活性。
使用 python 的 mutable-default-args "weirdness"(我不会称它为 "a feature")应该被认为是 一个 hack。
你是不是混淆了模型和视图?
UI 元素(例如按钮)应该只委托给您的数据模型。因此,如果您有一个具有持久状态的模型(即 class 具有属性),您可以只在其中实现一个 class 方法,该方法在单击按钮时处理所需的事情。
如果您尝试将有状态的事物绑定到您的演示文稿 (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
。这种统一访问常被称为context.
我们可以使用 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 increament(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.increament(val)
def some_more(val, context=None):
if context:
context.increament(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)
如果你不想设置一个class,你唯一的1其他选项是一个全局变量。您不能将它保存到局部变量,因为该命令从 mainloop
内运行,而不是在创建它的局部范围内运行。
例如:
button0 = Button(root, text="demo", command=lambda: increment_and_save(val))
def increment_and_save(val):
global saved
saved = increment(val)
1 并非字面意思,因为您可以使用各种其他方式来保存数据,例如数据库或文件,但我假设您想要一个 in-memory解决方案。
对我来说,上下文是我需要在调用修改该值的函数之间保留的单个 int 值的信息。我可以使用全局变量,但我知道这是不鼓励的。现在,我使用了包含 int 的列表形式的默认参数,并利用了可变性,以便在调用之间保留对值的更改,就像这样--
def increment(val, saved=[0]):
saved[0] += val
# do stuff
这个函数正在通过 tkinter 附加到按钮上,像这样~
button0 = Button(root, text="demo", command=lambda: increment(val))
这意味着没有 return 值可以分配给函数外的局部变量。
人们通常如何处理这种情况?我的意思是,当然,可变性技巧有效,但是如果我需要从多个函数访问和修改该值怎么办?
如果不设置一个带有静态方法和内部属性等的 class 就不能做到这一点吗?
使用 class。使用实例成员来保持状态。
class Incrementable:
def __init__(self, initial_value = 0):
self.x = initial_value
def increment(self, val):
self.x += val
# do stuff
您可以添加一个 __call__
方法来模拟函数调用(例如,如果您需要 backward-compatible)。这是否是一个好主意实际上取决于上下文和您的具体用例。
Can this not be done without setting up a class with static methods and internal attributes, etc?
可以,但不涉及 classes/objects 属性的解决方案不是 "pythonic"。在 python 中定义 classes 非常容易(上面的例子只有 5 行简单的代码),它给了你最大的控制和灵活性。
使用 python 的 mutable-default-args "weirdness"(我不会称它为 "a feature")应该被认为是 一个 hack。
你是不是混淆了模型和视图?
UI 元素(例如按钮)应该只委托给您的数据模型。因此,如果您有一个具有持久状态的模型(即 class 具有属性),您可以只在其中实现一个 class 方法,该方法在单击按钮时处理所需的事情。
如果您尝试将有状态的事物绑定到您的演示文稿 (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
。这种统一访问常被称为context.
我们可以使用 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 increament(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.increament(val)
def some_more(val, context=None):
if context:
context.increament(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)
如果你不想设置一个class,你唯一的1其他选项是一个全局变量。您不能将它保存到局部变量,因为该命令从 mainloop
内运行,而不是在创建它的局部范围内运行。
例如:
button0 = Button(root, text="demo", command=lambda: increment_and_save(val))
def increment_and_save(val):
global saved
saved = increment(val)
1 并非字面意思,因为您可以使用各种其他方式来保存数据,例如数据库或文件,但我假设您想要一个 in-memory解决方案。