2013-04-22 3 views
3

Скажем, у меня есть следующие функции:Конфликтующие имена переменных и функций в Python

def xplusy(x, y): 
    return x+y 

def xplus1(x): 
    xplusy = xplusy(x, 1) 
    return xplusy 

Теперь, если я позвоню a = xplus1(4) он выдает следующее сообщение об ошибке:

UnboundLocalError: local variable 'xplusy' referenced before assignment 

Ошибка из-за конфликт имен, если я переопределить xplus1 следующим образом:

def xplus1(x): 
    s = xplusy(x, 1) 
    return s 

работает отлично.

Почему это так: не может ли компилятор правильно различать переменную и вызов функции?

Любые дороги вокруг него?

+3

Почему усложнять свою жизнь? Просто дайте ему другое имя. – Maroun

+1

Вы можете просто «возвращать xplusy (x, 1)», например: 'def xplus1 (x): return xplusy (x, 1)' –

+0

Часто бывает полезно делать и широко использовать на других языках. Это не проблема, чтобы изменить его, но если речь идет о добавлении пары символов в код, я в порядке с ним. Почему это происходит теоретически, также неясно мне ... – sashkello

ответ

12

В Python функции - это данные, а типизация - динамическая.Это означает, что следующие строки действительны Python:

def func(x): 
    return x + 3 

func = 3 

func теперь является внутр. Исходная функция func больше не ссылается. Тот факт, что func изначально был функцией, не имеет никакого отношения к тому, какие типы данных могут быть присвоены ему в будущем. (Это то, что означает «динамическая типизация»).

Поэтому, поскольку нет статического ввода, а «функция» является допустимым типом данных, интерпретатор Python не имеет смысла различать функцию и кусок данных, на которые ссылается одноименное имя. Поэтому в рамках данной области невозможно использовать одно и то же неквалифицированное имя переменной, чтобы обозначать две разные вещи.

В вашем конкретном случае, если код в вашей xplus1 функции означает, что-нибудь, это будет означать «вычислить значение xplusy(x,1) и назначить того, что значение переменной xplusy - тем самым теряя ссылку на функцию xplusy «. Тем не менее, в рамках функции интерпретатор не позволит вам назначить переменную за пределами этой области, поэтому предполагает, что, написав инструкцию присваивания, вы вводите новую локальную переменную xplusy. Однако локальная переменная еще не определена, поэтому ваша попытка называть ее, xplusy(x,1), терпит неудачу. Глобально определенная функция не вызывается как спад, потому что, опять же, вы не можете иметь два неквалифицированных имени одинаково и указывать на разные данные в той же области.


Другой пример не демонстрирует «отсутствие дублирования имен переменных в пределах одной сферы» правил (которые я на самом деле только обнаруженных во время игры вокруг с подсказкой в ​​моей попытке построить этот ответ):

>>> def f1(): 
...  a = xplusy(3,4) 
...  xplusy = 5 
...  print xplusy 
... 
>>> f1() 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
    File "<stdin>", line 2, in f1 
UnboundLocalError: local variable 'xplusy' referenced before assignment 
>>> def f1(): 
...  a = xplusy(3,4) 
...  print a 
... 
>>> f1() 
7 

Это демонстрирует, что это действительно сфера, а не заявление, требующее уникальных имен.


EDIT: Это действительно здорово, что сообщение объясняет это и другое поведение рамочный связанные: http://me.veekun.com/blog/2011/04/24/gotcha-python-scoping-closures/

+0

Спасибо, лучшее объяснение на данный момент ... – sashkello

+0

Это правда с именами модулей? Я поддерживаю скрипт python, который имеет переменную, которая имеет то же имя, что и модуль. (время, если вам интересно.) – Jack

+0

@Jack Я не уверен, что вы просите. Верно, что операторы 'import' присваивают именам модулей (которые являются * не * именами переменных) для переменных, поэтому на них не влияют глобальные или локальные переменные, которые имеют одинаковое имя как модуль; то есть 'time = 3; от времени импорта времени как epoch_time; время печати; print epoch_time() 'будет печатать' 3', за которым следует количество секунд с эпохи. Однако, время = 3; от времени импорта времени; print time' будет печатать '<встроенное время функции>', так как переменная 'time' переназначается оператором import. –

1

Причина этого в том, что в вашей области видимости существует xplusy, как глобальная переменная, которая не может быть изменена, если вы не указали явно xplusy - global.

def xplusy(x,y): 
    return x+y 

def xplus1(x): 
    global xplusy 
    xplusy = xplusy(x,1) 
    return xplusy 

однако, это заставит xplusy относиться к int, float или любой xplusy вернулся в первый раз, а это означает, что после того, как в первый раз он будет бросать TypeError с.

Чем больше вещий способ сделать это, скорее всего, будет

def xplus1(x): 
    return xplusy(x,1) 

или с помощью functools модуля:

from functools import partial 
xplus1 = partial(xplusy,y=1) #since you wanted to override y with 1 

, если вы не заботитесь о которых преодолено аргумент, вы могли бы просто do

xplus1 = partial(xplusy,1) 
1

Имеются случаи, например, с обратными вызовами, которые вы указываете к функциям по их статическому имени (funcName вместо funcName()). Таким образом, в Python это имя переменной зарезервировано для функции. Очень похоже на то, как вы будете использовать функции LAMBDA, которые вы храните в переменной.

3

В функции python это объект первого класса, который означает, что он как любой другой объект.

Более подробную информацию о What are “first class” objects?

+4

Ну, функции также являются первоклассными объектами в Common Lisp, но Common Lisp имеет отдельное пространство имен для функций. –

1

В Python, xplusy может ссылаться на что угодно. Вы можете сделать это:

def xplusy(x, y): 
    return x+y 

def otherfunction(x, y): 
    return x*y 

def xplus1(x): 
    xplusy = otherfunction 
    return xplusy(x, 1) 

И xplusy переменная будет ссылаться на otherfunction в xplus1 области.

xplus1(2) 
>>> 2 

Результат 2 * 1 вместо 2 + 1. Будьте осторожны с assignments.Use как много переменных, как вам нужно.

+0

Спасибо за это, выглядит довольно странно :) – sashkello

Смежные вопросы