在分配之前引用了Python2?

时间:2018-09-23 05:09:51

标签: python-2.7 scope instance-variables

这为我提供了p1而不是count的分配前引用错误?

class Bagadclass:
    def __init__(self):
        print "i n s t a n t i a t e d"
        pass

    def getRegion(self):
        count = [0]
        p1 = [0 , 0]
        p2 = [0 , 0]

        def on_click(x,y,button,pressed):
            count[0] += 1

            if count[0] > 5:
                self.Image = pyautogui.screenshot(region=p1+p2)
                listener.stop()
                pass

            if count[0] == 2:
                p1 = list(pyautogui.position())
                pass

            if count[0] == 4:
                print p1
                p2 = (pyautogui.position()[0] - p1[0] , pyautogui.position()[1] - p1[1])
                pass

            print count , x , y , button , pressed
            pass

        with Listener(on_click=on_click) as listener:
            listener.join()
        pass

谁能解释为什么?我认为这与侦听器对象有关,但如果它适用于count,我认为它也应适用于其他变量。

1 个答案:

答案 0 :(得分:0)

函数on_click()是一个闭包,它可以访问在封闭范围count中定义的变量p1p2getRegion() 。这些变量(countp1p2)被称为 free变量 *,“如果代码块中使用了变量,则 在那里没有定义,它是一个自由变量” Link 1

*实际上,在您的示例中,只有count是自由变量; p1p2不是自由变量,因为您已经在on_click()的本地范围内对其进行了定义:

...

if count[0] == 2:
    p1 = list(pyautogui.position()) # <-- here
    pass

if count[0] == 4:
    print p1
    p2 = (pyautogui.position()[0] - p1[0] , pyautogui.position()[1] - p1[1]) # <-- and here

...

不再是自由变量,它们现在是局部变量。在每个中,名称可以是局部变量全局变量自由变量。名称不会在赋值语句后从自由变量转换为局部变量,而是,如果赋值语句出现在块内的任何位置,则该名称现在是局部变量。如果您尝试访问尚未绑定的局部变量(即代码执行尚未到达该局部变量的赋值语句),则会收到UnboundLocalError异常。

来自Execution model — Python 2.7.15 documentation

  

如果在代码块内的任何地方进行了名称绑定操作,则该块内对该名称的所有使用均被视为对当前块的引用。在绑定名称之前在块中使用名称时,这可能导致错误。这条规则很微妙。 Python缺少声明,并且允许名称绑定操作发生在代码块内的任何位置。可以通过扫描代码块的整个文本以进行名称绑定操作来确定代码块的局部变量。

从本质上讲,这基本上意味着您可以“访问”自由变量(包括对其进行修改/突变),但不能“写入” /重新绑定它们,Python会将其解释为定义了一个全新的局部变量。在Python 3中,您可以使用nonlocal关键字解决此问题,否则common practice会将您的数据包装在另一个结构(例如字典或列表)中,然后修改该结构以更改您的数据;这样,您就不会尝试重新绑定自由变量,而只是修改它所引用的对象。

这实际上是对count变量所做的操作,实际计数值存储在单个元素列表中,并且当on_click()需要增加计数时,它会增加第一个元素count列表(count[0]+=1)中的内容,而不是尝试重新绑定它(类似count+=1的方法将不起作用)。


如何修改代码的示例(# !表示更改):

...

    def getRegion(self):
        count = [0]
        p = [[0, 0], [0, 0]]  # [p1, p2]  # !
        def on_click(x,y,button,pressed):
            count[0] += 1
            if count[0] > 5:
                self.Image = pyautogui.screenshot(region=p[0]+p[1])  # !

...

            if count[0] == 2:
                p[0] = list(pyautogui.position())  # !

...

            if count[0] == 4:
                print p[0] # !
                p[1] = (pyautogui.position()[0] - p[0][0],
                        pyautogui.position()[1] - p[0][1])  # !

...

链接:

  1. Execution model — Python 2.7.15 documentation
  2. Why am I getting an UnboundLocalError when the variable has a value? — Python 2.7.15 documentation
  3. How to define free-variable in python?
  4. python counter with closure