具有相同参数的函数的全局与参数

2024-05-19 21:37:58 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在阅读“Making games with Python&Pygame”,我注意到,由于作者使用了许多函数来分解代码,因此他使用了许多全局变量,如GRIDSIZEBACKGROUNDCOLOR。我总是被告知全局函数通常是不好的,但是没有它们,每个绘图函数都会有10个以上的重复参数——我也被告知重复是不好的。我想知道,作者使用globals来表示大多数(绘图)函数中出现的参数是正确的,还是应该使用更多的重复参数。你知道吗


Tags: 函数代码绘图参数with作者全局pygame
3条回答

Globals通常是不好的,因为在一个巨大的程序中很难跟踪值。这在Python中没有那么糟糕,因为“全局”变量是模块级的,但即使在这种情况下,您也最好避免使用它们:您的模块可能变得庞大(使全局变量难以跟踪),模块级变量很容易被其他模块更新等等

我不是说你不应该使用它们-你应该在需要的时候使用它们。两种常见的情况是共享态和常数。你知道吗

共享状态的模块级变量

例如,模块级变量是存储整个模块甚至整个程序共享的状态的最佳方式。假设我们要实现一个游戏的渲染器;它应该存储所有程序共享的所有图像的地图。所以在我们的模块中,我们可以这样做:

# map_renderer.py

_map = [
    [None, None, None, None],
    [None, None, None, None],
    [None, None, None, None],
    [None, None, None, None]
]

def put_sprint(sprint, x, y):
    _map[x][y] = sprint

def get_sprint(x, y):
    return _map[x][y]

现在您甚至可以编写如下模块:

# background.py
import map_renderer as mr

def draw_background(background_images):
   for i, row in enumerate(background_images):
       for j, image in enumerate(row):
           put_sprint(image, i, j)

…在另一个模块中做类似的事情

# npc.py
import map_renderer as mr

def draw_npc(npc, x, y):
    image = npc.image
    put_sprint(image, x, y)

因为两个模块应该更新同一个映射,所以可以使用模块级变量来完成。你知道吗

注意对于那些更倾向于设计模式的人,我们可以说模块级变量是实现单例的一种非常有效和简单的方法。你知道吗

作为常量的模块级变量

然而,您书中的代码是使用模块级变量的另一种好方法的示例:常量。(我假设这些全局变量实际上是常量,因为它们遵循PEP 8中常量的样式。)

例如,考虑BACKGROUNDCOLOR变量。我想这个值在整个模块中都是一样的,也许在整个程序中都是一样的。假设这个值是0xFA2BEE。然后你有一些选择:

  • 你可以(理论上)在任何地方写下它的值:

    def paint_frame(image, x, y, h, w, fx, fy):
        paint_background(0xFA2BEE, fx, fy)
        spring = slice_image(image, x, y, x+w, y+h)
        paint(image, px, py)
    
    def clear_frame(fx, fy):
        paint_background(0xFA2BEE, fx, fy)
    

    然而,这显然是个坏主意。首先,弗里克是什么?任何阅读你的代码的人——即使是将来的你——都会被它弄糊涂。另外,如果背景应该改成0xB2BAA3呢?现在您必须在代码中找到所有0xFA2BEE,确保它是background的值而不是相同的其他值,然后替换它。不太好,对吧?

  • 如果这不好,你可以这样想:既然我应该避免使用“全局”变量,但是原始值不好用,我可以把它们作为参数传递!你知道吗

    def paint_frame(image, x, y, h, w, fx, fy, background):
        paint_background(background, fx, fy)
        spring = slice_image(image, x, y, x+w, y+h)
        paint(image, px, py)
    
    def clear_frame(fx, fy, background):
        paint_background(background, fx, fy)
    

    好吧,至少现在你0xFA2BEEpaint_frame/clear_frame的最后一个参数是背景的值。但是,您在一些已经很复杂的函数中添加了另一个参数。另外,如果您更改了背景,则必须在整个代码库中找到对paint_frame/clear_frame的所有调用。不酷,兄弟。你知道吗

    相反,你可以。。。

  • …创建常量!事实上,作者就是这么做的:

    BACKGROUND = 0xFA2BEE
    
    def paint_frame(image, x, y, h, w, fx, fy):
        paint_background(BACKGROUND, fx, fy)
        spring = slice_image(image, x, y, x+w, y+h)
        paint(image, px, py)
    
    def clear_frame(fx, fy):
        paint_background(BACKGROUND, fx, fy)
    

    现在,您1)不需要猜测0xFA2BEE是背景值还是其他值,2)您的函数保持简单,3)您可以只在一个地方轻松更改背景值。如您所见,模块级变量的使用在这里得到了很好的回报。即使您需要在大多数而不是所有位置使用相同的背景值,常数也会很有用:只需将其设为background参数的默认值:

    BACKGROUND = 0xFA2BEE
    
    def paint_frame(image, x, y, h, w, fx, fy, background=BACKGROUND):
        paint_background(background, fx, fy)
        spring = slice_image(image, x, y, x+w, y+h)
        paint(image, px, py)
    
    def clear_frame(fx, fy, background=BACKGROUND):
        paint_background(background, fx, fy)
    

    现在你打电话,让我们说paint_frame(img, i, j, FRAME_SIZE, FRAME_SIZE, k*FRAME_SIZE, l*FRAME_SIZE)大多数时间,只有在需要时才传递背景参数

常数也适合作为全局变量/模块级变量,因为它们不会改变。全局变量特别讨厌,因为您应该跟踪它们的值。但是,如果值不变,这就不是问题了,对吗?你知道吗

:根据惯例,这些变量是常量,可以更新。Python没有只读变量但是您可以通过遵循代码标准来避免问题。)

唯一的普遍规则是“视情况而定”

你会发现很多人说“不要用X”,“不要用Y”,“总是用Z”等等。好吧,大多数时候这些建议都是正确的,但是你可能总会发现它们不适用的上下文。“不要使用全局变量”规则就是这样。在有些情况下,使用global是最佳选择。即使在C语言中也有这样的情况,Python还有许多其他的情况可以这样做,因为模块级变量仍然比全局变量更安全。你知道吗

避免全局变量(或模块级变量)的建议是一个很好的建议,特别是对于新手。然而,正如你所看到的,有些情况下最好不要遵循它。你知道吗

在Python中,globals没有那么大的问题,因为Python globals在模块级别。(因此,global有点用词不当。)在许多情况下使用模块级global是很好的,因为真正的global是不合适的。你知道吗

书籍中的示例代码与产品代码不同

书籍和文章中的代码解释事情时要切中要害,不要让读者淹没在可能是噪音或与特定主题无关的内容中。你知道吗

作者采取了捷径,使代码尽可能地针对他们所解释的问题。你知道吗

也就是说,有比使用模块级变量更好的方法来做他们正在做的事情。甚至在书和教程中。你知道吗

巨大的状态机是坏的

包含太多范围的变量,不管它们处于什么级别,往往会产生副作用,即创建不可见的副作用,这些副作用会从应用程序变成的巨大状态机中显现出来。你知道吗

函数参数过多是一种代码气味

它应该告诉您可能需要更好地封装数据。应该将多个相关参数分组到单个数据结构中,并将该单个数据结构作为参数传递。你知道吗

相关问题 更多 >