Ситуация: вы пишете простую программу на Python, которая что-то считает по заданному алгоритму. Сначала всё идёт как обычно: объявляется глобальная переменная x, потом делаем новую функцию и обращаемся внутри неё к этой переменной:
x = 10
def sum():
x = x + 5
print(x)
Но при запуске этой функции командой sum() компьютер выдаёт ошибку:
Traceback (most recent call last):
File «main.py», line 6, in
sum()
File «main.py», line 3, in sum
x = x + 5
❌ UnboundLocalError: local variable ‘x’ referenced before assignment
Что это значит: Python ожидает, что x внутри функции будет локальной переменной. Соответственно, он ищет, где она объявляется в функции. А она не объявлена. Python падает с ошибкой.
Когда встречается: когда программист забыл про области видимости переменных или не использовал команду global
.
Что делать с ошибкой UnboundLocalError: local variable referenced before assignment
Главное — определиться с областью видимости и решить, какую переменную вы хотите использовать: локальную или глобальную.
Если вам нужна локальная переменная, то её нужно добавить внутрь функции. Имя переменной при этом может совпадать с именем такой же глобальной переменной, но она никак на неё не повлияет. В этом случае нужно в функцию добавить строку x = <значение>, например:
x = 10
def sum():
x = 11
x = x + 5
# выводим сумму с локальной переменной = 16
print(x)
sum()
# выводим значение глобальной переменной
# оно не поменялось и равно 10
print(x)
Если мы хотим внутри функции работать с глобальной переменной, то её также нужно добавить в функцию командой global
. После этого всё, что мы сделаем внутри функции с этой переменной, будет влиять и на значение глобальной переменной. Работает это так:
x = 10
def sum():
global x
x = x + 5
# выводим сумму с глобальной переменной = 15
print(x)
sum()
# выводим значение глобальной переменной
# оно поменялось и равно 15
print(x)
I don’t like this behavior, but this is how Python works. The question has already been answered by others, but for completeness, let me point out that Python 2 has more such quirks.
def f(x):
return x
def main():
print f(3)
if (True):
print [f for f in [1, 2, 3]]
main()
Python 2.7.6 returns an error:
Traceback (most recent call last):
File "weird.py", line 9, in <module>
main()
File "weird.py", line 5, in main
print f(3)
UnboundLocalError: local variable 'f' referenced before assignment
Python sees the f
is used as a local variable in [f for f in [1, 2, 3]]
, and decides that it is also a local variable in f(3)
. You could add a global f
statement:
def f(x):
return x
def main():
global f
print f(3)
if (True):
print [f for f in [1, 2, 3]]
main()
It does work; however, f becomes 3 at the end… That is, print [f for f in [1, 2, 3]]
now changes the global variable f
to 3
, so it is not a function any more.
Fortunately, it works fine in Python3 after adding the parentheses to print
.
Python treats variables referenced only inside a function as global variables. Any variable assigned to a function’s body is assumed to be a local variable unless explicitly declared as global.
Why Does This Error Occur?
Unboundlocalerror: local variable referenced before assignment
occurs when a variable is used before its created. Python does not have the concept of variable declarations. Hence it searches for the variable whenever used. When not found, it throws the error.
Before we hop into the solutions, let’s have a look at what is the global and local variables.
Local Variable Declarations vs. Global Variable Declarations
Local Variables | Global Variables |
---|---|
A local variable is declared primarily within a Python function. | Global variables are in the global scope, outside a function. |
A local variable is created when the function is called and destroyed when the execution is finished. | A Global Variable is created upon execution and exists in memory till the program stops. |
Local Variables can only be accessed within their own function. | All functions of the program can access global variables. |
Local variables are immune to changes in the global scope. Thereby being more secure. | Global Variables are less safer from manipulation as they are accessible in the global scope. |
Local Variable Referenced Before Assignment Error with Explanation
Try these examples yourself using our Online Compiler.
Let’s look at the following function:
# Variable declaration outside function (global scope) myVar = 10 def myFunction(): if myVar == 5: print("5") elif myVar == 10: print("10") elif myVar >= 15: print("greater than 15 ") # reassigning myVar in the function myVar = 0 myFunction()
Output
Explanation
The variable myVar
has been assigned a value twice. Once before the declaration of myFunction
and within myFunction
itself.
Solutions
Using Global Variables
Passing the variable as global allows the function to recognize the variable outside the function.
myVar = 10 def myFunction(): # Telling the function to consider myVar as global global myVar if myVar == 5: print("5") elif myVar == 10: print("10") elif myVar >= 15: print("greater than 15 ") myVar = 0 myFunction()
Output
10
Create Functions that Take in Parameters
Instead of initializing myVar
as a global or local variable, it can be passed to the function as a parameter. This removes the need to create a variable in memory.
# Passing myVar as a parameter in the function def myFunction(myVar): if myVar == 5: print("5") elif myVar == 10: print("10") elif myVar >= 15: print("greater than 15 ") myVar = 0 myFunction(10)
Output
10
UnboundLocalError: local variable ‘DISTRO_NAME’
This error may occur when trying to launch the Anaconda Navigator in Linux Systems.
Upon launching Anaconda Navigator, the opening screen freezes and doesn’t proceed to load.
$ anaconda-navigator
Traceback (most recent call last):
File "/home/user/anaconda3/lib/python3.7/site-packages/anaconda_navigator/widgets/main_window.py", line 541, in setup
self.post_setup(conda_data=conda_data)
...
...
UnboundLocalError: local variable 'DISTRO_NAME' referenced before assignment
Solution 1
Try and update your Anaconda Navigator with the following command.
conda update anaconda-navigator
Solution 2
If solution one doesn’t work, you have to edit a file located at
.../anaconda3//lib/python3.7/site-packages/anaconda_navigator/api/external_apps/vscode.py
After finding and opening the Python file, make the following changes:
In the function on line 159, simply add the line:
DISTRO_NAME = None
Save the file and re-launch Anaconda Navigator.
The program takes information from a form filled out by a user. Accordingly, an email is sent using the information.
from django import forms # Creating a Class for django forms class MyForm(forms.Form): name = forms.CharField(required=True) email = forms.EmailField(required=True) msg = forms.CharField( required=True, widget=forms.Textarea ) ... # Create a function to retrieve info from the form def GetContact(request): form_class = ContactForm if request.method == 'POST': form = form_class(request.POST) # Upon verifying validity, get following info and email with said info if form.is_valid(): name = request.POST.get('name') email = request.POST.get('email') msg = request.POST.get('msg') send_mail('Subject here', msg, email, ['[email protected]'], fail_silently=False) return HttpResponseRedirect('blog/inicio') return render(request, 'blog/inicio.html', {'form': form}) ...
Upon running you get the following error:
local variable 'form' referenced before assignment
Explanation
We have created a class myForm
that creates instances of Django forms. It extracts the user’s name, email, and message to be sent.
A function GetContact
is created to use the information from the Django form and produce an email. It takes one request
parameter. Prior to sending the email, the function verifies the validity of the form. Upon True
, .get()
function is passed to fetch the name, email, and message. Finally, the email sent via the send_mail
function
Why does the error occur?
We are initializing form
under the if request.method == “POST” condition statement. Using the GET request, our variable form
doesn’t get defined.
Local variable Referenced before assignment but it is global
This is a common error that happens when we don’t provide a value to a variable and reference it. This can happen with local variables. Global variables can’t be assigned.
This error message is raised when a variable is referenced before it has been assigned a value within the local scope of a function, even though it is a global variable.
Here’s an example to help illustrate the problem:
x = 10 def my_func(): print(x) x += 1 my_func()
In this example, x is a global variable that is defined outside of the function my_func(). However, when we try to print the value of x inside the function, we get a UnboundLocalError with the message “local variable ‘x’ referenced before assignment”.
This is because the += operator implicitly creates a local variable within the function’s scope, which shadows the global variable of the same name. Since we’re trying to access the value of x before it’s been assigned a value within the local scope, the interpreter raises an error.
To fix this, you can use the global keyword to explicitly refer to the global variable within the function’s scope:
x = 10 def my_func(): global x print(x) x += 1 my_func()
However, in the above example, the global keyword tells Python that we want to modify the value of the global variable x, rather than creating a new local variable. This allows us to access and modify the global variable within the function’s scope, without causing any errors.
Local variable ‘version’ referenced before assignment ubuntu-drivers
This error occurs with Ubuntu version drivers. To solve this error, you can re-specify the version information and give a split as 2 –
version = int(p_name.split('-')[-2])
Here, p_name means package name.
FAQs
How would you avoid using a global variable in multi-threads?
With the help of the threading module, you can avoid using global variables in multi-threading. Make sure you lock and release your threads correctly to avoid the race condition.
What is UnboundLocalError?
When a variable that is created locally is called before assigning, it results in Unbound Local Error in Python. The interpreter can’t track the variable.
Conclusion
Therefore, we have examined the local variable referenced before the assignment Exception in Python. The differences between a local and global variable declaration have been explained, and multiple solutions regarding the issue have been provided.
Trending Python Articles
-
[Solved] typeerror: unsupported format string passed to list.__format__
●May 31, 2023
-
Solving ‘Remote End Closed Connection’ in Python!
by Namrata Gulati●May 31, 2023
-
[Fixed] io.unsupportedoperation: not Writable in Python
by Namrata Gulati●May 31, 2023
-
[Fixing] Invalid ISOformat Strings in Python!
by Namrata Gulati●May 31, 2023
Toggle table of contents sidebar
Распространенные проблемы/нюансы работы с функциями#
Список/словарь в который собираются данные в функции, создан за пределами функции#
Очень часто в решении заданий встречается такой нюанс: функция должна собрать какие-то данные в список/словарь
и список создан вне функции. Тогда вроде как функция работает правильно,
но при этом тест не проходит. Это происходит потому что в таком варианте функция
работает неправильно и каждый вызов добавляет элементы в тот же список:
In [1]: result = [] In [2]: def func(items): ...: for i in items: ...: result.append(i*100) ...: return result ...: In [3]: func([1, 2, 3]) Out[3]: [100, 200, 300] In [4]: func([7, 8]) Out[4]: [100, 200, 300, 700, 800]
Исправить это можно переносом строки создания списка в функцию:
In [20]: def func(items): ...: result = [] ...: for i in items: ...: result.append(i*100) ...: return result ...: In [21]: func([1, 2, 3]) Out[21]: [100, 200, 300] In [22]: func([7, 8]) Out[22]: [700, 800]
Всё, что относится к функции лучше всегда писать внутри функции.
Тест тут не проходит потому что внутри файла задания функция вызывается первый
раз — всё правильно, а потом тест вызывает её второй раз и там вдруг в два раза больше данных чем нужно.
Значения по умолчанию в параметрах создаются во время создания функции#
Пример функции, которая должна выводить текущую дату и время при каждом вызове:
In [1]: from datetime import datetime In [2]: import time In [3]: def print_current_datetime(ptime=datetime.now()): ...: print(f">>> {ptime}") ...: In [4]: for i in range(3): ...: print("Имитируем долгое выполнение...") ...: time.sleep(1) ...: print_current_datetime() ...: Имитируем долгое выполнение... >>> 2021-02-23 09:01:49.845425 Имитируем долгое выполнение... >>> 2021-02-23 09:01:49.845425 Имитируем долгое выполнение... >>> 2021-02-23 09:01:49.845425
Так как datetime.now()
указано в значении по умолчанию,
это значение создается во время создания функции и в итоге при каждом вызове
время одно и то же. Для корректного вывода, надо вызывать datetime.now()
в теле функции:
In [5]: def print_current_datetime(): ...: print(f">>> {datetime.now()}") ...:
Второй пример где этот нюанс может привести к неожиданным результатам,
если о нем не знать — изменяемые типы данных в значении по умолчанию.
Например, использование списка в значении по умолчанию:
In [15]: def add_item(item, data=[]): ...: data.append(item) ...: return data ...:
В этом случае список data создается один раз — при создании функции и
при вызове функции, данные добавляются в один и тот же список.
В итоге все повторные вызовы будут добавлять элементы:
In [16]: add_item(1) Out[16]: [1] In [17]: add_item(2) Out[17]: [1, 2] In [18]: add_item(4) Out[18]: [1, 2, 4]
Если нужно сделать так, чтобы параметр data был необязательным и по умолчанию
создавался пустой список, надо сделать так:
In [22]: def add_item(item, data=None): ...: if data is None: ...: data = [] ...: data.append(item) ...: return data ...: In [23]: add_item(1) Out[23]: [1] In [24]: add_item(2) Out[24]: [2]
Ошибка UnboundLocalError: local variable referenced before assignment#
Ошибка может возникнуть в таких случаях:
-
обращение к переменной в функции идет до ее создания — это может быть
случайность (ошибка) или следствие того, что какое-то условие не выполнилось -
обращение внутри функции к глобальной переменной, но при этом внутри функции
создана такая же переменная позже
Первый случай — обратились к переменной до ее создания:
def f(): print(b) b = 55 In [6]: f() --------------------------------------------------------------------------- UnboundLocalError Traceback (most recent call last) Input In [6], in <cell line: 1>() ----> 1 f() Input In [5], in f() 1 def f(): ----> 2 print(b) 3 b = 55 UnboundLocalError: local variable 'b' referenced before assignment
Переменная создается в условии, а условие не выполнилось:
def f(): if 5 > 8: b = 55 print(b) In [8]: f() --------------------------------------------------------------------------- UnboundLocalError Traceback (most recent call last) Input In [8], in <cell line: 1>() ----> 1 f() Input In [7], in f() 2 if 5 > 8: 3 b = 55 ----> 4 print(b) UnboundLocalError: local variable 'b' referenced before assignment
Имя глобальной и локальной переменной одинаковое и внутри функции сначала идет
попытка обращения к глобальной, потом создание локальной:
a = 10 def f(): print(a) a = 55 print(a) In [4]: f() --------------------------------------------------------------------------- UnboundLocalError Traceback (most recent call last) Input In [4], in <cell line: 1>() ----> 1 f() Input In [3], in f() 1 def f(): ----> 2 print(a) 3 a = 55 4 print(a) UnboundLocalError: local variable 'a' referenced before assignment
Python должен определить область видимости переменной. И в случае функций, Python
считает, что переменная локальная, если она создана внутри функции.
На момент когда мы доходим до вызова функции, Python уже решил, что a
это
локальная переменная и именно из-за этого первая строка print(a)
дает ошибку.
Некоторые не совсем очевидные вещи, с которыми сталкиваются начинающие программисты Python.
Почему я получаю исключение UnboundLocalError, хотя переменная имеет значение?
Может показаться неожиданным получить UnboundLocalError в ранее работающем коде, в который добавили операцию присваивания где-то внутри функции.
Этот код:
>>> x = 10 >>> def bar(): ... print(x) >>> bar() 10
работает, но следующий код:
>>> x = 10 >>> def foo(): ... print(x) ... x += 1
приводит к UnboundLocalError:
>>> foo() Traceback (most recent call last): ... UnboundLocalError: local variable 'x' referenced before assignment
Это происходит потому, что, когда вы делаете присваивание переменной в области видимости, она становится локальной в этой области и скрывает другие переменные с таким же именем во внешних областях.
Когда последняя инструкция в foo присваивает новое значение переменной x, компилятор решает, что это локальная переменная. Следовательно, когда более ранний print пытается напечатать неинициализированную переменную, возникает ошибка.
В примере выше можно получить доступ к переменной, объявив её глобальной:
>>> x = 10 >>> def foobar(): ... global x ... print(x) ... x += 1 >>> foobar() 10
Это явное объявление требуется для того, чтобы напомнить вам, что (в отличие от внешне аналогичной ситуации с переменными класса и экземпляра), вы на самом деле, изменяете значение переменной во внешней области видимости:
>>> print(x) 11
Вы можете сделать подобную вещь во вложенной области видимости использованием ключевого слова nonlocal:
>>> def foo(): ... x = 10 ... def bar(): ... nonlocal x ... print(x) ... x += 1 ... bar() ... print(x) >>> foo() 10 11
В Python, переменные, на которые только ссылаются внутри функции, считаются глобальными. Если переменной присваивается новое значение где-либо в теле функции, считается, что она локальная, и, если вам нужно, то нужно явно указывать её глобальной.
Хотя это немного удивительно на первый взгляд, это легко объяснимо. С одной стороны, требование global для присваиваемых переменных предотвращает непреднамеренные побочные эффекты в bar. С другой стороны, если global был обязательным для всех глобальных ссылок, вы бы использовали global все время. Вы должны были бы объявить как глобальную каждую ссылку на встроенную функцию или компонент импортируемого модуля.
Почему анонимные функции (lambda), определенные в цикле с разными значениями, возвращают один и тот же результат?
Например, вы написали следующий код:
>>> squares = [] >>> for x in range(5): ... squares.append(lambda: x**2)
Это даёт вам список из 5 функций, считающих x**2. Можно ожидать, что, будучи вызванными, они вернут, соответственно, 0, 1, 4, 9, и 16. Однако, вы увидите, что все они возвращают 16:
>>> squares[2]() 16 >>> squares[4]() 16
Это случается, поскольку x не является локальной для lambda, а определена во внешней области видимости, и получается тогда, когда она вызывается — а не когда определяется.
В конце цикла, x=4, поэтому все функции возвращают 4**2, то есть 16. Это можно также проверить, изменив значение x и посмотрев на результат:
>>> x = 8 >>> squares[2]() 64
Чтобы избежать подобного, необходимо сохранять значения переменных локально:
>>> squares = [] >>> for x in range(5): ... squares.append(lambda n=x: n**2)
Здесь, n=x создаёт локальную для функции переменную n и вычисляется в момент определения функции:
>>> squares[2]() 4 >>> squares[4]() 16
Это применимо не только к анонимным, а также и к обычным функциям.
Как организовать совместный доступ к глобальным переменным для нескольких модулей?
Канонический способ организовать подобный доступ — это создать отдельный модуль (часто называемый config или cfg). Просто добавьте import config в каждый модуль приложения. При этом модуль становится доступен через глобальное имя. Поскольку существует только один экземпляр модуля, любые изменения, произведённые в модуле отражаются везде. Например:
config.py:
x = 0
mod.py:
import config config.x = 1
main.py:
import config import mod print(config.x)
По тем же соображениям, модули можно использовать как основу для имплементации синглтона.
Как правильнее использовать импортирование?
В общих случаях не используйте from modulename import *. Это засоряет пространство имён того, кто импортирует. Некоторые люди избегают этой идиомы даже для тех немногих модулей, которые были спроектированны, чтобы так импортироваться. Это такие модули как Tkinter и threading.
Импортируйте модули в начале файла. Это отвечает на вопрос, какие модули требует Ваш код и находится ли имя модуля в области видимости. Запись по одному импорту на строку упрощает добавление и удаление операторов импорта, но множественный импорт будет занимать меньше места на экране.
Хорошая практика, если Вы импортируете модули в следующем порядке:
- стандартные библиотечные модули (например, sys, os, getopt, re)
- модули сторонних разработчиков (всё, что установлено в директории site-packages) — например, PIL, NumPy и т.д.
- локально созданные модули
Иногда бывает необходимо поместить импорт в функцию или класс, чтобы избежать проблем с циклическим импортом. Gordon McMillan советует:
Циклический импорт отлично работает, если оба модуля используют форму
import <module>
. Но они терпят неудачу, когда второй модуль хочет извлечь имя из первого (
from module import name
) и импорт находится на внешнем уровне. Это происходит из-за того, что имена первого модуля ещё недоступны, так как первый модуль занят импортом второго.
В этом случае, если второй модуль используется только в одной функции, то импорт можно легко поместить в эту функцию. К тому времени, как он будет вызван, первый модуль уже закончит инициализацию и второй модуль осуществит свой импорт.
Может оказаться необходимым переместить импорт из начала файла, если один из модулей платформно-зависимый. В этом случае импорт всех модулей в начале файла окажется невозможным. В этой ситуации хорошим решением будет импорт нужных модулей в соответствующем платформно-зависимом коде.
Переносите импорт во вложенные области видимости, такие как определения функций, только если Вы столкнулись с проблемой, например циклического импорта, или если Вы пытаетесь сократить время инициализации модуля.
Эта техника полезна, если многие из импортов не являются необходимыми, и зависят от того, как программа будет исполняться. Вы также можете поместить импорт в функцию, если конкретные модули используются только в этой функции. Обратите внимание, что загрузить модуль в первый раз может оказаться дорого из-за задержки на инициализацию модуля, однако повторные загрузки «бесплатны», они стоят только пары поисков в словарях. Даже если имя модуля исчезло из области видимости, модуль скорее всего до сих пор находится в sys.modules.
Почему значения по умолчанию разделяются между объектами?
Этот тип ошибки часто встречается среди начинающих. Предположим, функция:
def foo(mydict={}): # Опасность: разделяемая ссылка между вызовами ... compute something ... mydict[key] = value return mydict
В первый раз, когда вы вызываете функцию, mydict содержит одно значение. Второй раз, mydict содержит 2 элемента, поскольку, когда foo() начинает выполняться, mydict уже содержит элемент.
Часто ожидается, что вызов функции создаёт новые объекты для значений по умолчанию. Но это не так. Значения по умолчанию создаются лишь однажды, когда функция определяется. Если этот объект изменяется, как словарь в нашем примере, последующие вызовы функции будут использовать изменённый объект.
По определению, неизменяемые объекты (числа, строки, кортежи и None), безопасны при изменении. Изменение изменяемых объектов, таких как словари, списки, и экземпляры пользовательских классов может привести к неожиданным последствиям.
Поэтому, хорошей практикой является не использовать изменяемые объекты в качестве значений по умолчанию. Вместо этого, используйте None и внутри функции, проверяйте аргумент на None и создавайте новый список/словарь. Например, не пишите:
def foo(mydict={}): ...
Но пишите так:
def foo(mydict=None): if mydict is None: mydict = {} # create a new dict for local namespace
Однако, эта особенность может быть полезна. Когда у вас есть функция, которая долго выполняется, часто применяемая техника — кэширование параметров и результата каждого вызова функции:
def expensive(arg1, arg2, _cache={}): if (arg1, arg2) in _cache: return _cache[(arg1, arg2)] # Расчёт значения result = ... expensive computation ... _cache[(arg1, arg2)] = result # Кладём результат в кэш return result
Как передать опциональные или именованные параметры из одной функции в другую?
Получить такие параметры можно с помощью спецификаторов * и ** в списке аргументов функции; они возвращают кортеж позиционных аргументов и словарь именованых параметров. После этого Вы можете передать их в другую функцию, используя в её вызове * и **:
def f(x, *args, **kwargs): ... kwargs['width'] = '14.3c' ... g(x, *args, **kwargs)
Почему изменение списка ‘y’ изменяет также список ‘x’?
Если вы написали код:
>>> x = [] >>> y = x >>> y.append(10) >>> y [10] >>> x [10]
вы, возможно, будете удивлены тому, что добавление в y изменяет также и x.
Два факта приводят к такому результату:
- Переменные — это просто ссылки на объекты. y = x не создаёт копию списка — это просто создаёт переменную y, которая ссылается на тот же объект, что и x.
- Списки изменяемы.
После вызова append, содержимое объекта было изменено с [] на [10]. Поскольку x и y ссылаются на один и тот же объект, использование любого из них даёт нам [10].
Если мы используем неизменяемые объекты:
>>> x = 5 # числа неизменяемы >>> y = x >>> x = x + 1 # 5 нельзя изменить. Мы создаём НОВЫЙ объект >>> x 6 >>> y 5
мы можем видеть, что x и y больше не равны, поскольку числа неизменяемы, и x = x + 1 не изменяет число 5 путем увеличения. Вместо этого, создаётся новый объект 6 и присваивается переменной x (то есть, изменяется то, на какой объект ссылается x). После этого у нас 2 объекта (6 и 5) и 2 переменные, которые на них ссылаются.
Некоторые операции (например y.append(10) и y.sort()) изменяют объект, в то время, как внешне похожие операции (например y = y + [10] и sorted(y)) создают новый объект. Вообще в Python (и во всех случаях в стандартной библиотеке), метод, который изменяет объект, возвращает None, чтобы помочь избежать ошибок. Поэтому, если вы написали
y = y.sort()
думая, что это даст вам отсортированную копию y, вы вместо этого получите None, что скорее всего приведёт к легко диагностируемой ошибке.
Однако, существует один класс операций, где одна и та же операция ведёт себя по-разному с различными типами: расширенные операторы присваивания. Например, += изменяет списки, но не кортежи или числа (a_list += [1, 2, 3] эквивалентно a_list.extend([1, 2, 3])) и изменяет список, в то время, как some_tuple += (1, 2, 3) и some_int += 1 создают новый объект.
Если вы хотите знать, ссылаются ли 2 переменные на один объект или нет, вы можете использовать оператор is, или встроенную функцию id.
Как создавать функции более высокого порядка?
Есть два пути: использовать вложенные функции или вызываемые объекты. Например, с использованием вложенных функций:
def linear(a, b): def result(x): return a * x + b return result
Использование вызываемого объекта:
class linear: def __init__(self, a, b): self.a, self.b = a, b def __call__(self, x): return self.a * x + self.b
В обоих случаях,
taxes = linear(0.3, 2)
даёт функцию, что (к примеру) taxes(10e6) == 0.3 * 10e6 + 2.
Использование вызываемого объекта — немного медленнее, и в результате получается больше кода. Однако, заметьте, что несколько функций могут разделять свою сигнатуру с помощью наследования:
class exponential(linear): # __init__ наследуется def __call__(self, x): return self.a * (x ** self.b)
Объект может сохранять свое состояние для нескольких вызовов:
class counter: value = 0 def set(self, x): self.value = x def up(self): self.value = self.value + 1 def down(self): self.value = self.value - 1 count = counter() inc, dec, reset = count.up, count.down, count.set
Здесь inc, dec, reset выступают в роли функций, которые разделяют одну и ту же переменную.
Как скопировать объект в Python?
В общем случае, с помощью модуля copy.
Некоторые объекты можно скопировать более просто. Словари имеют метод copy:
newdict = olddict.copy()
Последовательности могут быть скопированы путём срезов:
new_l = l[:]
Как узнать доступные методы и атрибуты объекта?
dir(x) возвращает список методов и атрибутов.
Как можно узнать имя объекта?
Вообще говоря, никак, поскольку объекты в действительности не имеют имён. Важно: присваивание всегда связывает имя с объектом. Это верно и для инструкций def и class.
>>> class A: ... pass ... >>> B = A >>> >>> a = B() >>> b = a >>> print(b) <__main__.A object at 0x7fbcc3ee5160> >>> print(a) <__main__.A object at 0x7fbcc3ee5160>
Возможно, класс имеет имя: однако, хотя он связан с двумя именами и запрашивается через имя B, созданный экземпляр всё ещё считается экземпляром класса A. Однако, невозможно сказать, имя экземпляра a или b, поскольку оба они связаны с одним и тем же значением.
Какой приоритет у оператора «запятая»?
Запятая не является оператором в Python.
>>> "a" in "b", "a" (False, 'a')
Поскольку запятая — не оператор, но разделитель между выражениями, пример выше исполняется как если бы было введено:
("a" in "b"), "a"
А не
"a" in ("b", "a")
То же самое верно и для операторов присваивания (=, += и другие). Они не являются операторами как таковыми, а лишь синтаксическими разделителями в операциях присваивания.
Есть ли в Python эквивалент тернарного оператора «?:» в C?
Да. Синтаксис:
[on_true] if [expression] else [on_false]
x, y = 50, 25 small = x if x < y else y
Можно ли писать обфусцированные однострочники?
Можно.
from functools import reduce # Простые числа < 1000 print(list(filter(None,map(lambda y:y*reduce(lambda x,y:x*y!=0, map(lambda x,y=y:y%x,range(2,int(pow(y,0.5)+1))),1),range(2,1000))))) # Первые 10 чисел Фибоначчи print(list(map(lambda x,f=lambda x,f:(f(x-1,f)+f(x-2,f)) if x>1 else 1: f(x,f), range(10)))) # Множество Мандельброта print((lambda Ru,Ro,Iu,Io,IM,Sx,Sy:reduce(lambda x,y:x+y,map(lambda y, Iu=Iu,Io=Io,Ru=Ru,Ro=Ro,Sy=Sy,L=lambda yc,Iu=Iu,Io=Io,Ru=Ru,Ro=Ro,i=IM, Sx=Sx,Sy=Sy:reduce(lambda x,y:x+y,map(lambda x,xc=Ru,yc=yc,Ru=Ru,Ro=Ro, i=i,Sx=Sx,F=lambda xc,yc,x,y,k,f=lambda xc,yc,x,y,k,f:(k<=0)or (x*x+y*y >=4.0) or 1+f(xc,yc,x*x-y*y+xc,2.0*x*y+yc,k-1,f):f(xc,yc,x,y,k,f):chr( 64+F(Ru+x*(Ro-Ru)/Sx,yc,0,0,i)),range(Sx))):L(Iu+y*(Io-Iu)/Sy),range(Sy ))))(-2.1, 0.7, -1.2, 1.2, 30, 80, 24)) # ___ ___/ ___ ___/ | | |__ lines on screen # V V | |______ columns on screen # | | |__________ maximum of "iterations" # | |_________________ range on y axis # |____________________________ range on x axis
Не пытайтесь это делать дома!
Почему -22 // 10 равно -3?
Поскольку i % j имеет тот же знак, что j. А ещё
i == (i // j) * j + (i % j)
Как можно изменить строку?
Никак, поскольку строки неизменяемы. В большинстве ситуаций, нужно просто сделать новую строку из различных частей. Однако, если так нужно, можно использовать io.StringIO, либо модуль array:
>>> import io >>> s = "Hello, world" >>> sio = io.StringIO(s) >>> sio.getvalue() 'Hello, world' >>> sio.seek(7) 7 >>> sio.write("there!") 6 >>> sio.getvalue() 'Hello, there!' >>> import array >>> a = array.array('u', s) >>> print(a) array('u', 'Hello, world') >>> a[0] = 'y' >>> print(a) array('u', 'yello, world') >>> a.tounicode() 'yello, world'
Как использовать строки для вызова функций/методов?
Существует несколько приёмов.
- Лучший — использование словаря, ставящего соответствие строке функцию. Его главное достоинство — строки не обязаны совпадать с названиями функций.
def a(): pass def b(): pass dispatch = {'go': a, 'stop': b} # Note lack of parens for funcs dispatch[get_input()]()
- Использование встроенной функции getattr:
import foo getattr(foo, 'bar')()
- Использование locals или eval (не рекомендуется)
def myFunc(): print("hello") fname = "myFunc" f = locals()[fname] f() f = eval(fname) f()
Как удалить все символы новой строки в конце строки?
Можно использовать S.rstrip(«rn») для удаления символов новой строки, без удаления конечных пробелов:
>>> lines = ("line 1 rn" ... "rn" ... "rn") >>> lines.rstrip("rn") 'line 1 '
Как создать многомерный список?
Возможно, вы попробуете этот неудачный вариант:
>>> A = [[None] * 2] * 3
Это выглядит правильно, если напечатать:
>>> A [[None, None], [None, None], [None, None]]
Но если вы присвоите значение, то оно появится в нескольких местах:
>>> A[0][0] = 5 >>> A [[5, None], [5, None], [5, None]]
Причина в том, что оператор * не создаёт копию, а только ссылку на существующий объект. *3 создаёт список из 3 ссылок на один и тот же список. Изменение в одной строке изменяют другие, что, вероятно, не то, что вы хотите.
Возможные пути решения:
A = [None] * 3 for i in range(3): A[i] = [None] * 2
w, h = 2, 3 A = [[None] * w for i in range(h)]
Или, можно использовать специальные модули, предоставляющие матрицы. Наиболее известным является NumPy.
Почему a_tuple[i] += [‘item’] не работает, а добавление работает?
Это из-за того, что расширенный оператор присваивания — оператор присваивания, а также из-за разницы между изменяемыми и неизменяемыми объектами в Python.
Это обсуждение относится в общем, когда расширенные операторы присваивания применяются к элементам кортежа, которые указывают на изменяемые объекты, но мы будем использовать список и +=, как образец.
Если вы напишете:
>>> a_tuple = (1, 2) >>> a_tuple[0] += 1 Traceback (most recent call last): ... TypeError: 'tuple' object does not support item assignment
Причина исключения должна быть понятна: 1 добавляется к объекту a_tuple[0], но когда мы пытаемся присвоить результат, 2, к первому элементу в кортеже, мы получаем ошибку, поскольку мы не можем изменить элемент кортежа.
То есть, это выражение делает следующее:
>>> result = a_tuple[0] + 1 >>> a_tuple[0] = result Traceback (most recent call last): ... TypeError: 'tuple' object does not support item assignment
Когда мы пишем что-то вроде:
>>> a_tuple = (['foo'], 'bar') >>> a_tuple[0] += ['item'] Traceback (most recent call last): ... TypeError: 'tuple' object does not support item assignment
Исключение немного более неожиданное, но более удивителен тот факт, что, несмотря на ошибку, элемент добавился!
>>> a_tuple[0] ['foo', 'item']
Чтобы понять, что случилось, нужно знать, что:
- Если объект определяет метод __iadd__, он вызывается, когда выполняется +=, и возвращенное значение используется для присваивания
- Для списков, __iadd__ эквивалентен вызову extend для списка
Таким образом,
>>> a_list = [] >>> a_list += [1] >>> a_list [1]
Эквивалентен:
>>> result = a_list.__iadd__([1]) >>> a_list = result
Таким образом, наш пример с кортежом эквивалентен:
>>> result = a_tuple[0].__iadd__(['item']) >>> a_tuple[0] = result Traceback (most recent call last): ... TypeError: 'tuple' object does not support item assignment
__iadd__ завершился успешно, и список увеличился, но присваивание законилось ошибкой.