如何在Python中在函数之间共享变量?

25

我有两个函数,fun1fun2,分别以字符串和数字作为输入。 它们也都使用相同的变量a作为输入。 这是代码:

a = ['A','X','R','N','L']

def fun1(string,vect):
    out = []
    for letter in vect:
        out. append(string+letter)
    return out

def fun2(number,vect):
    out = []
    for letter in vect:
        out.append(str(number)+letter)
    return out

x = fun1('Hello ',a)
y = fun2(2,a)

这些函数执行一些无意义的操作。我的目标是以这样的方式重写代码,使变量a在函数之间共享,这样它们就不再将其作为输入。

消除变量a作为输入的一种方法是在函数内部定义它,但不幸的是,这不是非常优雅。 有什么可能实现我的目标的方法吗?

这些函数应该以相同的方式运行,但输入参数只应该是字符串和数字(fun1(string)fun2(number))。


4
你已经完成了一半的对象式编程重学之路。 - chepner
3
函数内部可以“看到”变量 a。请尝试使用 for letter in a: - Patrick Haugh
5个回答

33

面向对象编程在这里有所帮助:

class MyClass(object):
    def __init__(self):
        self.a = ['A','X','R','N','L']  # Shared instance member :D

    def fun1(self, string):
        out = []
        for letter in self.a:
            out.append(string+letter)
        return out

    def fun2(self, number):
        out = []
        for letter in self.a:
            out.append(str(number)+letter)
        return out

a = MyClass()
x = a.fun1('Hello ')
y = a.fun2(2)

2
现在你可以删除 vect 参数了。 ;) - Tagc
1
如果a是一个常量,最好将其作为类属性(可能是元组)。否则,将其传递给MyClass初始化程序可能更好。 - bruno desthuilliers
每次调用fun1或fun2时,是否都会重复定义self.a操作?我问这个问题是因为如果self.a是在一个更加复杂和要求更高的操作之后获得的,我希望它只执行一次,而不是每次调用fun1和fun2。 - Federico Gentile
2
请注意,这个答案,*由@Willem Van Onsem提供的答案以及由@involtus提供的答案,这三个答案都是正确和可行的,所以我会给它们点赞。然而,按照“好坏”顺序,最佳*答案是由@ospahiu提供的面向对象方案,采用共享成员变量;第二好的答案是由@Willem Van Onsem提供的建议,可以称之为“自动作用域”的内部外部变量读取解决方案;而由@involtus提供的global方案则是最不推荐使用的解决方案。 - Gabriel Staples
@GabrielStaples 后两个答案实际上是同一件事。你所谓的“自动作用域”仍然是全局的——只是当值不被重新分配时,global关键字是不必要的。虽然通常认为重新分配全局变量是导致全局变量问题的原因,但有些问题仅仅通过访问它们是无法解决的。 - Karl Knechtel

18

除了使用类之外的一种替代方案:您可以使用 global 关键字来使用函数外部存在的变量。

a = 5
def func():
    global a
    return a+1

print (func())

这将会打印出6。

但是应该尽量避免使用全局变量。


8

由于 a 在函数定义之前在函数作用域之外被定义,因此您不需要将其作为参数传递。您可以直接使用 a

Python首先查找变量是否在函数作用域中定义,如果没有,则查找该作用域之外。

a = ['A','X','R','N','L']

def fun1(string):
    out = []
    for letter in a:
        out.append(string+letter)
    return out

def fun2(number):
    out = []
    for letter in a:
        out.append(str(number)+letter)
    return out

x = fun1('Hello ')
y = fun2(2)

在这种情况下,您还可以将函数改写成更优雅的列表推导式:
a = ['A','X','R','N','L']

def fun1(string):
    return [string+letter for letter in a]

def fun2(number):
    return [str(number)+letter for letter in a]

x = fun1('Hello ')
y = fun2(2)

3
这真的被推荐吗?还是应该避免? - Ma0
2
@Ev.Kounis:这种方法并不是非常优雅,因为人们可能会忘记在作用域外定义变量,并在函数中引入a。因此,最好使用面向对象编程。 - Willem Van Onsem
2
@Ev.Kounis:除非“a”是一个常量(在整个过程生命周期内不应该更改),否则应该明确避免使用它——即使这样,如果你关心可测试性,明确传递“a”仍然会更好。 - bruno desthuilliers
我并不是说这个答案是正确的方法。我只是想向OP指出在这里传递a作为参数是无用的。但是通常来说,定义全局变量并不是一个好主意。 - Willem Van Onsem
请注意,有时您没有选择。例如,tkinter 回调函数通常只接受一个或零个参数,因此您无法传递应用程序状态。因此,回调函数必须使用全局范围中的应用程序状态。请注意,面向对象编程在这里并没有真正帮助。在 tkinter 中编写 UI 类的常见模式通常将整个程序状态放在该类中,其中创建了一个实例。这基本上归结为相同的事情。 - Roland Smith

0

这可以很容易地通过使用global关键字来实现。这使得a变量在整个文件中都可用。然而,应尽量避免使用全局变量,因为每个函数都可以访问它们,这使得越来越难以确定哪些函数实际上读取和写入这些变量。

a = ['A','X','R','N','L']

def fun1(string):
    out = []
    for letter in a:
        out. append(string+letter)
    return out

def fun2(number):
    out = []
    for letter in a:
        out.append(str(number)+letter)
    return out

x = fun1('Hello ')
y = fun2(2,a)

那么您提议的答案是什么? - pedram bashiri

0

面向对象编程,将a作为成员变量绝对是最好的解决方案。

但有时您会遇到非面向对象的代码,例如Flask应用程序,当您有多个端点需要共享某些值时。 在这种情况下,共享或全局变量是正确的选择。这意味着定义变量在所有方法的范围之外,以便可以在任何地方访问它。

现在,如果您的变量值永远不会改变,则应使用大写字母进行命名,以标记其为最终和某种全局变量(类似于Java中的最终静态变量)。

A = ['A','X','R','N','L']

但如果值确实会更改,则首先名称应为小写 a = ['A','X','R','N','L']。其次,您希望限制可以更改值的位置,理想情况下仅限于一个方法,在那里可以使用global关键字更改该值。

a = ['A','X','R','N','L']

def fun1(string,vect):
    global a
    a.append('W')

    out = []
    for letter in vect:
        out. append(string+letter)
    return out

def fun2(number,vect):
    out = []
    for letter in vect:
        out.append(str(number)+letter)
    return out

x = fun1('Hello ',a)
y = fun2(2,a)

如果你发现在代码中多处更改变量 a 的值,那么共享/全局变量可能不是你想要的,相反,你应该将该值作为参数传递。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接