【问题标题】:The rule of thumb as to when we should use a global variable in a function关于何时应该在函数中使用全局变量的经验法则
【发布时间】:2020-09-12 13:18:49
【问题描述】:

当我用函数编写游戏代码时,我经常对将哪个变量设为全局变量感到困惑。

我听说全球化变量不是一个很好的做法,所以我尝试通过不全球化任何变量来最小化数量,并且只全球化错误消息告诉我的那些。但是这样做很烦人,而且浪费时间。

谁能告诉我什么时候应该在函数中全局化一个变量,

什么时候不需要?

这是我的意思的一个示例(功能):
import turtle
from random import randint as rd
from time import sleep
delay = 0.1

wn = turtle.Screen()
wn.setup(400,400)
wn.tracer(0)

player = turtle.Turtle('square')
player.penup()
player.goto(0,-170)

rock = turtle.Turtle('circle')
rock.shapesize(0.5,0.5)
rock.penup()
rock.goto(rd(-190,190),200)

rocks = [rock]

pen = turtle.Turtle(visible=False)
pen.penup()
pen.goto(0,150)

def go_left(): # No globalizing here
    if player.xcor() >= -170:
        player.setx(player.xcor()-10)

def go_right(): # No globalizing here
    if player.xcor() <= 170:
        player.setx(player.xcor()+10)

def move_rocks(): # No globalizing here
    for rock in rocks:
        rock.sety(rock.ycor()-rd(0,2))

def loop_rocks():
    global count # rocks not globalized here
    for rock in rocks:
        if rock.ycor() < -200:
            count += 1
            rock.goto(rd(-190,190),200)

def add_rocks():
    global count # rocks not globalized here
    if count == len(rocks) and len(rocks) <= 15:
        rock = turtle.Turtle('circle')
        rock.shapesize(0.5,0.5)
        rock.penup()
        rock.goto(rd(-190,190),200)
        rocks.append(rock)
        count = 0

def write_score():
    global time,score,high_score # pen not globalized here
    time += 1
    if time == 500:
        score += 1
        time = 0
    if score > high_score:
        high_score = score

    pen.clear()
    pen.write(f'Score: {score}   High Score: {high_score}',align='center',font=('Arial',10,'bold'))

def hit(): # No globalizing here
    for rock in rocks:
        if player.distance(rock) < 15:
            return True

def die():
    global score,rocks # player not globalized here
    sleep(1)
    for rock in rocks:
        rock.goto(300,300)
    rocks = rocks[:1]
    rocks[0].goto(rd(-190,190),200)
    player.goto(0,-170)
    score = 0

wn.listen()
wn.onkeypress(go_left,'Left')
wn.onkeypress(go_right,'Right')

score = 0
high_score = 0
count = 0
time = 0

while True:
    if hit():
        die()
    move_rocks()
    loop_rocks()
    add_rocks()
    write_score()
    wn.update()

【问题讨论】:

  • 如果可以的话,永远不要使用全局变量。有时,全局“常量”很好。但是例如,go_left 使用全局变量,但它应该改为将这些值作为函数的参数 与其余函数相同。
  • 我同意@juanpa.arrivillaga 的观点,尽可能避免使用全局变量——全局constants,其值通常不会变化,实际上是件好事。您通常可以通过将它们作为参数传递给函数来避免它们,或者使它们成为类的方法可以通过它们都自动接收的self 第一个参数访问的类的属性。
  • @martineau 您能否详细说明为什么这不是一个好主意?为了避免隐藏变量名?避免意外更改可变类型?抱歉,即使我不是 OP,也有后续问题。只是好奇。
  • 嗨,安禅。我钦佩你在其他地方的惊人耐心工作。我的评论没有讽刺意味,这与你的问题无关。我想帮助和保护你。因此,请允许我确保您知道此 meta.stackoverflow.com/questions/258206/what-is-a-help-vampire 如果您阅读了它但不知道我指的是什么,请随时忽略我的输入。

标签: python function global-variables turtle-graphics python-turtle


【解决方案1】:

样式规则不是语言规则。 IE。你不应该使用eval(),但它在语言中。

告诉我什么时候我们应该在一个变量中全局变量的经验法则 功能,什么时候不需要?

何时使用global 的规则很简单,但即使是网络上的教程也会出错。

  1. global 关键字不应用于创建全局 变量。

(是的,这部分是样式 规则。)当您在函数外部定义顶级变量时,Python 将其设为全局变量。 (您不要为此使用global 关键字。)当您分配函数内的变量时,Python 假定它是函数的本地变量。当您想要更改以后的假设时,您只需要 global 关键字,以便您可以从函数内重新分配 (=) 全局变量。您不需要 global 声明来检查全局变量。您不需要它来调用可能会更改其内部状态或内容的全局变量的方法:

  1. 当您想要重新分配 (=) 时,您只需要 global 关键字 函数内的全局变量。

global 声明用于重新分配全局变量的任何函数。它位于对变量、访问或赋值的第一个引用之前。为了简单和风格,全局语句放在函数的开头。

像“你不应该使用全局变量”这样的声明是一种风格规则,并且适用于大多数编程语言——如果/当你可以的话,应用它。如果你绝对不能,不要为此感到难过,只是:

  1. 评论您正确使用的所有全局变量。

全局常量不是问题:

  1. 如果全局常量是真正的常量,它们永远不需要global 关键字。

@juanpa.arrivillaga 的 go_left() 示例将附加值作为参数而不是全局变量,没有考虑到 go_left() 是一个 回调 并且海龟事件分配函数不'不提供额外的参数。 (他们应该,但他们没有。)我们可以使用 lambda 表达式(或 functools 中的 partial 函数)来解决这个问题,但是当以这种方式使用时,lambda is'恕我直言,这也不是特别棒的风格。

@martineau 的建议“使它们成为类的方法可以访问的类的属性”(又名 类变量)很好,但没有说的是它意味着 子类化 Turtle 或 wrapping 使用另一个类的 turtle 实例。

我个人对可变全局变量的看法是,它们在多线程世界中存在问题。

【讨论】:

  • 即使没有partial,您也不需要使用 lambda 来部分应用参数,您只需使用常规函数即可。虽然可变的全局状态在多线程程序中可能是灾难性的,但它不仅仅是一个样式规则,它是用于设计所有语言的程序的基本反模式,即使不考虑多线程,它也很容易导致难以推理,难以测试,意大利面条代码。请注意,您仍然可以使用没有子类化turtle 的类,确实,这就是@martineau 的想法,相反您可以执行MyObject(state1, state2).callback 之类的操作
【解决方案2】:

虽然这不是一个答案,但我只想指出在从外部范围/全局变量中隐藏名称时要注意的另一件事。 cdlane 在their answer 中写道

您不需要global 声明来检查全局变量。

我认为它比这更进一步,因为你不能那样使用 global 关键字,因为它是一个声明。正如 cdlane 已经说过的,它用于将局部范围(例如函数或类)中的变量声明为全局范围,以便您可以从局部范围为这些变量分配新值。您甚至可以使用 gobal 关键字从本地范围声明 new 全局变量,尽管正如 cdlane 指出的那样,这又不是一个好主意。以下是一些突出显示这些行为的代码:

a = c = 1  # define global variables 'a' and 'b' and assign both the
# value 1.


class Local:
    def __init__(self):
        print(a)  # if you only want to examine the global variable 'a',
        # you don't need any keywords
        self.declare_variables()

    def declare_variables(self):
        global a, b  # declare 'a' and 'b' as global variables, such that any
        # assignment statements in this scope refer to the global variables
        a = 2  # reassign a new value to the already existing global
        # variable 'a'.
        b = 3  # assign a value to the previously undeclared global variable
        # 'b'. you should avoid this.
        c = 4  # this does not affect the global variable 'c', since the
        # variable 'c' in this scope is assumed to be local by default.


local = Local()
print(a, b, c)  # the vaules of the gobal variables 'a' and 'b' have changed,
# while 'c' remains unaffected.

到目前为止,还没有什么新东西。但是,当您是来自全局变量的shadowing the names,但仍在同一范围内的其他地方访问全局变量时,这将成为一个问题。

  • 如果您在尝试访问该全局变量之前声明了一个隐藏全局变量名称的变量,那么在该声明之后对该变量名称的所有引用都将引用该局部变量。我认为这可能是更糟糕的情况,因为这可能未被检测到并且不会产生任何错误,但会返回错误的结果。
  • 如果您尝试声明一个新的局部变量,或使用具有相同变量名称的global 关键字之后您已经在同一范围内引用了该变量名称,则会导致UnboundLocalErrorSyntaxError
def reference_global_before_local_declaration():
    print(a)  # first reference the global variable 'a'. this statement would
    # work on its own if 'a' wouldn't be redeclared to be a local variable 
    # later on.
    a = 5  # redeclare 'a' to be a local variable and assign it the value 5.


reference_global_before_local_declaration()


def reference_global_before_global_declaration():
    print(a)  # first reference the global variable 'a'. this statement would
    # work on its own if 'a' wouldn't be declared to be a global variable
    # again later on.
    global a  # declare 'a' to be a global variable again.


reference_global_before_global_declaration()


def reference_global_after_local_declaration():
    a = 'text'  # redeclare 'a' to be a local variable of type string and
    # assign it the value 'text'.
    b = a + 1  # here, 'a' was supposed to reference the global variable 
    # 'a', but is now referencing the local variable 'a' instead, due to 'a'
    # being declared in the same scope and shadowing the name of the gobal 
    # variable 'a'.


reference_global_after_local_declaration()

我知道避免这种情况的唯一方法是使用globals() 函数,尽管这确实违背了所有目的,我不会推荐它。不过,我建议阅读 PEP 3104 - Access to Names in Outer Scopes,它讨论了这类问题并提出了解决方案,但最终从未实施。

def reference_global_before_local_declaration():
    print(globals()['a'])
    a = 5


reference_global_before_local_declaration()


def reference_global_before_global_declaration():
    print(globals()['a'])
    global a


reference_global_before_global_declaration()


def reference_global_after_local_declaration():
    a = 'text'
    b = globals()['a'] + 1


reference_global_after_local_declaration()

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-01-20
    • 1970-01-01
    • 2020-10-05
    • 2018-12-18
    • 1970-01-01
    • 1970-01-01
    • 2022-01-16
    相关资源
    最近更新 更多