Содержание страницы
ЧЗВ по программированию¶Programming FAQ
Содержание
- Вопросы по программированию
- Общие вопросы
- Существует ли отладчик на уровне исходного кода с точками останова, пошаговым выполнением и т. д.?
- Существует ли инструмент для поиска ошибок или выполнения статического анализа?Is there a tool to help find bugs or perform static analysis?
- Как создать автономный исполняемый файл из скрипта Python?How can I create a stand-alone binary from a Python script?
- Существуют ли стандарты кодирования или руководство по стилю для программ на Python?Are there coding standards or a style guide for Python programs?
- Ядро языка
- Почему возникает UnboundLocalError, если переменная имеет значение?
- Каковы правила для локальных и глобальных переменных в Python?What are the rules for local and global variables in Python?
- Почему лямбда-функции, определённые в цикле с разными значениями, возвращают один и тот же результат?Why do lambdas defined in a loop with different values all return the same result?
- Как разделять глобальные переменные между модулями?How do I share global variables across modules?
- Каковы «лучшие практики» использования import в модуле?What are the “best practices” for using import in a module?
- Как передать необязательные или именованные параметры из одной функции в другую?How can I pass optional or keyword parameters from one function to another?
- В чём разница между аргументами и параметрами?What is the difference between arguments and parameters?
- Как написать функцию с выходными параметрами (передача по ссылке)?How do I write a function with output parameters (call by reference)?
- Как создать функцию высшего порядка в Python?How do you make a higher order function in Python?
- Как скопировать объект в Python?How do I copy an object in Python?
- Как найти методы или атрибуты объекта?How can I find the methods or attributes of an object?
- Как моему коду узнать имя объекта?How can my code discover the name of an object?
- Что не так с приоритетом оператора запятой?What’s up with the comma operator’s precedence?
- Существует ли эквивалент тернарного оператора ”?:” из C?Is there an equivalent of C’s ”?:” ternary operator?
- Можно ли писать запутанные однострочники на Python?Is it possible to write obfuscated one-liners in Python?
- Числа и строки
- Как указать шестнадцатеричные и восьмеричные целые числа?
- Почему -22 // 10 возвращает -3?Why does -22 // 10 return -3?
- Как преобразовать строку в число?How do I convert a string to a number?
- Как преобразовать число в строку?How do I convert a number to a string?
- Как изменить строку на месте?How do I modify a string in place?
- Как с помощью строк вызывать функции и методы?How do I use strings to call functions/methods?
- Существует ли аналог функции chomp() из Perl для удаления завершающих символов новой строки из строк?Is there an equivalent to Perl’s chomp() for removing trailing newlines from strings?
- Существует ли аналог функций scanf() или sscanf()?Is there a scanf() or sscanf() equivalent?
- Что означает ошибка UnicodeDecodeError или UnicodeEncodeError?What does ‘UnicodeDecodeError’ or ‘UnicodeEncodeError’ error mean?
- Производительность
- Моя программа слишком медленная. Как её ускорить?
- Какой способ конкатенации множества строк самый эффективный?What is the most efficient way to concatenate many strings together?
- Последовательности (кортежи/списки)
- Как преобразовывать кортежи в списки и обратно?
- Что такое отрицательный индекс?What’s a negative index?
- Как перебрать последовательность в обратном порядке?How do I iterate over a sequence in reverse order?
- Как удалить дубликаты из списка?How do you remove duplicates from a list?
- Как создать массив в Python?How do you make an array in Python?
- Как создать многомерный список?How do I create a multidimensional list?
- Как применить метод к последовательности объектов?How do I apply a method to a sequence of objects?
- Словари
- Как сделать так, чтобы ключи словаря отображались в постоянном порядке?
- Я хочу выполнить сложную сортировку: можно ли в Python реализовать преобразование Шварца?I want to do a complicated sort: can you do a Schwartzian Transform in Python?
- Как отсортировать один список по значениям из другого списка?How can I sort one list by values from another list?
- Объекты
- Что такое класс?
- Что такое метод?What is a method?
- Что такое self?What is self?
- Как проверить, является ли объект экземпляром данного класса или его подкласса?How do I check if an object is an instance of a given class or of a subclass of it?
- Что такое делегирование?What is delegation?
- Как вызвать метод, определённый в базовом классе, из производного класса, который его переопределяет?How do I call a method defined in a base class from a derived class that overrides it?
- Как организовать код, чтобы было проще изменить базовый класс?How can I organize my code to make it easier to change the base class?
- Как создать статические данные класса и статические методы класса?How do I create static class data and static class methods?
- Как перегрузить конструкторы (или методы) в Python?How can I overload constructors (or methods) in Python?
- Я пытаюсь использовать __spam и получаю ошибку о _SomeClassName__spam.I try to use __spam and I get an error about _SomeClassName__spam.
- Мой класс определяет __del__, но он не вызывается при удалении объекта.My class defines __del__ but it is not called when I delete the object.
- Как получить список всех экземпляров данного класса?How do I get a list of all instances of a given class?
- Модули
- Как создать .pyc-файл?
- Как узнать имя текущего модуля?How do I find the current module name?
- Как сделать так, чтобы модули импортировали друг друга?How can I have modules that mutually import each other?
- __import__(‘x.y.z’) возвращает <module ‘x’>; как получить z?__import__(‘x.y.z’) returns <module ‘x’>; how do I get z?
- При редактировании импортированного модуля и его повторном импорте изменения не отображаются. Почему это происходит?When I edit an imported module and reimport it, the changes don’t show up. Why does this happen?
- Общие вопросы
Общие вопросы¶General Questions
Существует ли отладчик на уровне исходного кода с точками останова, пошаговым выполнением и т. д.?¶Is there a source code level debugger with breakpoints, single-stepping, etc.?
Да.
The pdb module is a simple but adequate console-mode debugger for Python. It is part of the standard Python library, and is documented in the Library Reference Manual. You can also write your own debugger by using the code for pdb as an example.
Интерактивная среда разработки IDLE, входящая в стандартный дистрибутив Python (обычно доступна как Tools/scripts/idle), включает графический отладчик. Документация по отладчику IDLE находится по адресу http://www.python.org/idle/doc/idle2.html#Debugger.
PythonWin – это IDE для Python, включающая графический отладчик на основе pdb. Отладчик PythonWin раскрашивает точки останова и обладает рядом полезных возможностей, таких как отладка программ, не являющихся программами PythonWin. PythonWin доступен как часть проекта Python for Windows Extensions, а также в составе дистрибутива ActivePython (см. http://www.activestate.com/Products/ActivePython/index.html).
Boa Constructor – это среда разработки и конструктор графического интерфейса на основе wxWidgets. Он предоставляет визуальное создание и редактирование фреймов, инспектор объектов, различные представления исходного кода, включая обозреватели объектов, иерархии наследования, HTML-документацию, сгенерированную из строк документации, расширенный отладчик, встроенную справку и поддержку Zope.
Eric – это IDE, построенная на PyQt и компоненте редактирования Scintilla.
Pydb – это версия стандартного отладчика Python pdb, модифицированная для использования с DDD (Data Display Debugger) – популярным графическим интерфейсом отладчика. Pydb можно найти по адресу http://bashdb.sourceforge.net/pydb/, а DDD – по адресу http://www.gnu.org/software/ddd.
Существует ряд коммерческих IDE для Python, включающих графические отладчики. Среди них:
- Wing IDE (http://wingware.com/)
- Komodo IDE (http://www.activestate.com/Products/Komodo)
Существует ли инструмент для поиска ошибок или выполнения статического анализа?¶Is there a tool to help find bugs or perform static analysis?
Да.
PyChecker – это инструмент статического анализа, который находит ошибки в исходном коде Python и предупреждает о сложности и стиле кода. PyChecker можно получить по адресу http://pychecker.sf.net.
Pylint – ещё один инструмент, который проверяет, соответствует ли модуль стандарту кодирования, а также позволяет писать плагины для добавления собственных функций. В дополнение к поиску ошибок, выполняемому PyChecker, Pylint предлагает несколько дополнительных возможностей, таких как проверка длины строки, соответствие имён переменных вашему стандарту кодирования, полная реализация объявленных интерфейсов и другие. На странице http://www.logilab.org/card/pylint_manual приведён полный список возможностей Pylint.
Как создать автономный исполняемый файл из скрипта Python?¶How can I create a stand-alone binary from a Python script?
Вам не нужно уметь компилировать Python в C, если ваша цель – создать автономную программу, которую пользователи могут скачать и запустить без предварительной установки дистрибутива Python. Существует ряд инструментов, которые определяют набор модулей, необходимых программе, и связывают эти модули вместе с бинарным файлом Python, создавая единый исполняемый файл.
Один из способов – использовать инструмент freeze, который входит в исходный код Python как Tools/freeze. Он преобразует байт-код Python в массивы C; с помощью компилятора C вы можете встроить все свои модули в новую программу, которая затем связывается со стандартными модулями Python.
Он работает, рекурсивно сканируя ваш исходный код на наличие операторов import (в обеих формах) и ищет модули в стандартном пути Python, а также в каталоге исходного кода (для встроенных модулей). Затем он превращает байт-код модулей, написанных на Python, в код C (инициализаторы массивов, которые можно превратить в объекты кода с помощью модуля marshal) и создает специальный конфигурационный файл, содержащий только те встроенные модули, которые действительно используются в программе. После этого он компилирует сгенерированный код C и компонует его с остальной частью интерпретатора Python, формируя самодостаточный бинарный файл, который ведет себя точно так же, как ваш скрипт.
Очевидно, freeze требует компилятор C. Существует несколько других утилит, которые не требуют. Одна из них – py2exe Томаса Хеллера (только для Windows) по адресу
Ещё один – SQFREEZE Кристиана Тисмера, который добавляет байт-код к специально подготовленному интерпретатору Python, способному найти байт-код в исполняемом файле.
Другие инструменты включают Squeeze Фредрика Лунда и cx_Freeze Энтони Туйнинги.
Существуют ли стандарты кодирования или руководство по стилю для программ на Python?¶Are there coding standards or a style guide for Python programs?
Да. Требуемый стиль оформления кода для модулей стандартной библиотеки описан в документе PEP 8.
Ядро языка¶Core Language
Почему возникает UnboundLocalError, если переменная содержит значение?¶Why am I getting an UnboundLocalError when the variable has a value?
Может быть неожиданным получить ошибку 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) пытается вывести неинициализированную локальную переменную, возникает ошибка.
В приведённом выше примере можно получить доступ к переменной внешней области, объявив её глобальной:
>>> 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?¶What are the rules for local and global variables in Python?
В Python переменные, которые используются только внутри функции, неявно считаются глобальными. Если переменной присваивается новое значение в любом месте тела функции, она считается локальной. Если переменной когда-либо присваивается новое значение внутри функции, она неявно является локальной, и её нужно явно объявить как 'global'.
Хотя сначала это может показаться удивительным, небольшое размышление всё объясняет. С одной стороны, требование global для присваиваемых переменных предотвращает непреднамеренные побочные эффекты. С другой стороны, если бы global требовалось для всех глобальных ссылок, вам пришлось бы использовать global постоянно. Пришлось бы объявлять как глобальные каждую ссылку на встроенную функцию или компонент импортированного модуля. Такой беспорядок свёл бы на нет полезность объявления global для выявления побочных эффектов.
Почему лямбды, определённые в цикле с разными значениями, все возвращают один и тот же результат?¶Why do lambdas defined in a loop with different values all return the same result?
Предположим, что с помощью цикла for определяется несколько разных лямбд (или даже обычных функций), например:
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 не является локальной для лямбда-функций, а определена во внешней области видимости и доступна при вызове лямбды, а не при её определении. В конце цикла значение x равно 4, поэтому все функции теперь возвращают 4**2, то есть 16. Вы также можете убедиться в этом, изменив значение x и посмотрев, как изменятся результаты лямбд:
>>> x = 8
>>> squares[2]()
64
Чтобы избежать этого, нужно сохранить значения в переменных, локальных для лямбд, чтобы они не зависели от значения глобальной x:
squares = []
for x in range(5):
squares.append(lambda n=x: n**2)
Здесь n=x создаёт новую переменную n, локальную для лямбды и вычисляемую при её определении, так что она имеет то же значение, что и x в тот момент цикла. Это означает, что значение n будет равно 0 в первой лямбде, 1 во второй, 2 в третьей и так далее. Следовательно, каждая лямбда теперь вернёт правильный результат:
>>> squares[2]()
4
>>> squares[4]()
16
Обратите внимание: такое поведение присуще не только лямбдам, но и обычным функциям.
Каковы «лучшие практики» использования import в модуле?¶What are the “best practices” for using import in a module?
В общем случае не используйте from modulename import *. Это загромождает пространство имён импортирующего модуля. Некоторые избегают такого стиля даже для тех немногих модулей, которые были спроектированы для импорта таким образом. К таким модулям относятся tkinter и threading.
Импортируйте модули в начале файла. Так сразу становится ясно, какие ещё модули требуются вашему коду, и не возникает вопросов, находится ли имя модуля в области видимости. Использование одного импорта на строку упрощает добавление и удаление импортов, а использование нескольких импортов в одной строке экономит место на экране.
Рекомендуется импортировать модули в следующем порядке:
- модули стандартной библиотеки – например, sys, os, getopt, re
- модули сторонних библиотек (всё, установленное в каталог site-packages Python), например mx.DateTime, ZODB, PIL.Image и т.д.
- модули локальной разработки
Никогда не используйте относительные импорты пакетов. Если вы пишете код, находящийся в модуле package.sub.m1, и хотите импортировать package.sub.m2, не пишите просто from . import m2, даже если это допустимо. Вместо этого пишите from package.sub import m2. Подробнее см. PEP 328.
Иногда импорты приходится переносить внутрь функции или класса, чтобы избежать проблем с циклическими импортами. Gordon McMillan говорит:
Циклические импорты нормально работают, когда оба модуля используют форму импорта “import <module>”. Они перестают работать, когда второй модуль пытается получить имя из первого (“from module import name”), и импорт находится на верхнем уровне. Это происходит потому, что имена в первом модуле ещё недоступны, так как первый модуль занят импортом второго.
В этом случае, если второй модуль используется только в одной функции, импорт можно легко перенести в эту функцию. К моменту вызова импорта первый модуль уже завершит инициализацию, и второй модуль сможет выполнить свой импорт.
Также может потребоваться перенести импорты из верхнего уровня кода, если некоторые модули являются платформозависимыми. В таком случае может быть невозможно импортировать все модули в начале файла. Тогда хорошим решением будет импортировать нужные модули в соответствующем платформозависимом коде.
Переносите импорты в локальную область видимости, например внутрь определения функции, только если это необходимо для решения проблемы, такой как избежание циклического импорта или попытка сократить время инициализации модуля. Этот метод особенно полезен, если многие импорты не нужны в зависимости от того, как выполняется программа. Возможно, вы также захотите перенести импорты в функцию, если модули используются только внутри неё. Обратите внимание, что загрузка модуля в первый раз может быть дорогой из-за однократной инициализации модуля, но повторная загрузка модуля практически бесплатна – требуется лишь несколько поисков в словаре. Даже если имя модуля вышло из области видимости, модуль, вероятно, доступен в sys.modules.
Если модуль используется только экземплярами определённого класса, то разумно импортировать этот модуль в методе __init__ класса и затем присвоить модуль переменной экземпляра, чтобы модуль был всегда доступен (через эту переменную экземпляра) в течение времени жизни объекта. Обратите внимание: чтобы отложить импорт до момента создания экземпляра класса, импорт должен находиться внутри метода. Размещение импорта внутри класса, но вне какого-либо метода, всё равно приведёт к тому, что импорт произойдёт при инициализации модуля.
Как передать необязательные или именованные параметры из одной функции в другую?¶How can I pass optional or keyword parameters from one function to another?
Соберите аргументы с помощью спецификаторов * и ** в списке параметров функции; это даст позиционные аргументы в виде кортежа, а именованные – в виде словаря. Затем вы можете передать эти аргументы при вызове другой функции, используя * и **:
def f(x, *args, **kwargs):
...
kwargs['width'] = '14.3c'
...
g(x, *args, **kwargs)
В чём разница между аргументами и параметрами?¶What is the difference between arguments and parameters?
Параметры определяются именами, которые указаны в определении функции, тогда как аргументы – это значения, которые передаются функции при её вызове. Параметры определяют, какие типы аргументов может принимать функция. Например, для такого определения функции:
def func(foo, bar=None, **kwargs):
pass
foo, bar и kwargs – это параметры func. Однако при вызове func, например:
func(42, bar=314, extra=somevar)
значения 42, 314 и somevar являются аргументами.
Как написать функцию с выходными параметрами (передача по ссылке)?¶How do I write a function with output parameters (call by reference)?
Помните, что в Python аргументы передаются по присваиванию. Поскольку присваивание лишь создает ссылки на объекты, между именем аргумента в вызывающей и вызываемой функции нет псевдонима, а значит, нет и передачи по ссылке как таковой. Желаемого эффекта можно достичь несколькими способами.
Возвращая кортеж результатов:
def func2(a, b): a = 'new-value' # a и b – локальные имена b = b + 1 # присвоены новым объектам return a, b # вернуть новые значения x, y = 'old-value', 99 x, y = func2(x, y) print(x, y) # вывод: new-value 100
Это почти всегда самое понятное решение.
С помощью глобальных переменных. Это не потокобезопасно и не рекомендуется.
Передавая изменяемый (изменяемый на месте) объект:
def func1(a): a[0] = 'new-value' # 'a' ссылается на изменяемый список a[1] = a[1] + 1 # изменяет общий объект args = ['old-value', 99] func1(args) print(args[0], args[1]) # вывод: new-value 100
Передавая словарь, который будет изменён:
def func3(args): args['a'] = 'new-value' # args – изменяемый словарь args['b'] = args['b'] + 1 # изменить его на месте args = {'a':' old-value', 'b': 99} func3(args) print(args['a'], args['b'])
Или объединив значения в экземпляре класса:
class callByRef: def __init__(self, **args): for (key, value) in args.items(): setattr(self, key, value) def func4(args): args.a = 'new-value' # args – изменяемый callByRef args.b = args.b + 1 # изменить объект на месте args = callByRef(a='old-value', b=99) func4(args) print(args.a, args.b)
Почти никогда нет веской причины усложнять до такой степени.
Лучший выбор – вернуть кортеж, содержащий несколько результатов.
Как сделать функцию высшего порядка в Python?¶How do you make a higher order function in Python?
У вас есть два варианта: использовать вложенные области видимости или вызываемые объекты. Например, предположим, вы хотите определить linear(a,b), которая возвращает функцию f(x), вычисляющую значение a*x+b. Использование вложенных областей видимости:
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.
Подход с вызываемым объектом имеет недостаток: он немного медленнее и\nприводит к чуть более длинному коду. Однако обратите внимание, что набор вызываемых объектов\nможет разделять свою сигнатуру через наследование:
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?¶How do I copy an object in Python?
В общем случае попробуйте copy.copy() или copy.deepcopy(). Не все объекты можно копировать, но большинство можно.
Некоторые объекты копировать проще. У словарей есть метод copy():
newdict = olddict.copy()
Последовательности можно скопировать срезом:
new_l = l[:]
Как найти методы или атрибуты объекта?¶How can I find the methods or attributes of an object?
Для экземпляра x пользовательского класса dir(x) возвращает отсортированный по алфавиту список имён, содержащий атрибуты экземпляра, а также методы и атрибуты, определённые в его классе.
Как код может узнать имя объекта?¶How can my code discover the name of an object?
Вообще говоря, нет, потому что у объектов на самом деле нет имён. По сути, присваивание всегда связывает имя со значением; то же верно для инструкций def и class, но в этом случае значением является вызываемый объект. Рассмотрим следующий код:
class A:
pass
B = A
a = B()
b = a
print(b)
<__main__.A object at 0x16D07CC>
print(a)
<__main__.A object at 0x16D07CC>
Можно утверждать, что у класса есть имя: даже если он привязан к двум именам и вызывается через имя B, созданный экземпляр всё равно будет определён как экземпляр класса A. Однако невозможно сказать, какое имя у экземпляра – a или b, поскольку оба имени привязаны к одному и тому же значению.
Вообще говоря, коду не требуется «знать имена» конкретных значений. Если вы не пишете намеренно интроспективные программы, это обычно указывает на то, что стоит изменить подход.
В comp.lang.python Фредрик Лунд однажды привёл отличную аналогию в ответ на этот вопрос:
Точно так же, как вы узнаёте имя кота, которого нашли на крыльце: сам кот (объект) не может сказать вам своего имени, и ему на это всё равно – так что единственный способ узнать, как его зовут, – спросить всех соседей (пространства имён), не их ли это кот (объект)...
...и не удивляйтесь, если окажется, что он известен под многими именами – или вообще без имени!
Что не так с приоритетом оператора-запятой?¶What’s up with the comma operator’s precedence?
Запятая в Python – не оператор. Рассмотрим такой сеанс:
>>> "a" in "b", "a"
(False, 'a')
Поскольку запятая – не оператор, а разделитель между выражениями, приведённый выше код выполняется так, как если бы вы ввели:
>>> ("a" in "b"), "a"
а не:
>>> "a" in ("b", "a")
То же самое верно для различных операторов присваивания (=, += и т. д.). Они не являются операторами в полном смысле, а представляют собой синтаксические разделители в операторах присваивания.
Существует ли эквивалент тернарного оператора ”?:” из C?¶Is there an equivalent of C’s ”?:” ternary operator?
Да, существует. Синтаксис следующий:
[on_true] if [expression] else [on_false]
x, y = 50, 25
small = x if x < y else y
До введения этого синтаксиса в Python 2.5 распространённым приёмом было использование логических операторов:
[expression] and [on_true] or [on_false]
Однако этот приём небезопасен, так как может дать неверный результат, если on_true имеет ложное булево значение. Поэтому всегда лучше использовать форму ... if ... else ....
Можно ли писать запутанные однострочники на Python?¶Is it possible to write obfuscated one-liners in Python?
Да. Обычно это делается путём вложения lambda в lambda. Смотрите следующие три примера, автор Ulf Bartelt:
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))
# \___ ___/ \___ ___/ | | |__ линии на экране
# V V | |______ столбцы на экране
# | | |__________ максимум "iterations"
# | |_________________ диапазон по оси Y
# |____________________________ диапазон по оси X
Не пытайтесь повторить это дома, дети!
Числа и строки¶Numbers and strings
Как задать шестнадцатеричные и восьмеричные целые числа?¶How do I specify hexadecimal and octal integers?
Чтобы задать восьмеричное число, поставьте перед восьмеричным значением ноль, а затем строчную или прописную букву «o». Например, чтобы присвоить переменной «a» восьмеричное значение «10» (8 в десятичной системе), введите:
>>> a = 0o10
>>> a
8
Шестнадцатеричные числа задаются так же просто. Просто поставьте перед шестнадцатеричным числом ноль, а затем строчную или прописную «x». Шестнадцатеричные цифры можно указывать в нижнем или верхнем регистре. Например, в интерпретаторе Python:
>>> a = 0xa5
>>> a
165
>>> b = 0XB2
>>> b
178
Почему -22 // 10 возвращает -3?¶Why does -22 // 10 return -3?
Это в первую очередь обусловлено желанием, чтобы i % j имел тот же знак, что и j. Если вы хотите этого, а также хотите:
i == (i // j) * j + (i % j)
тогда целочисленное деление должно возвращать floor (целую часть с округлением вниз). C также требует выполнения этого тождества, и тогда компиляторы, которые обрезают i // j, должны сделать так, чтобы i % j имел тот же знак, что и i.
Существует мало реальных случаев использования i % j, когда j отрицательно. Когда j положительно, таких случаев много, и практически во всех из них полезнее, чтобы i % j было равно >= 0. Если часы показывают 10 сейчас, что они показывали 200 часов назад? -190 % 12 == 2 полезно; -190 % 12 == -10 – это ошибка, которая только и ждёт, чтобы проявиться.
Как преобразовать строку в число?¶How do I convert a string to a number?
Для целых чисел используйте встроенный конструктор типа int(), например int('144') == 144. Аналогично float() преобразует в число с плавающей точкой, например float('144') == 144.0.
По умолчанию эти функции интерпретируют число как десятичное, так что int('0144') == 144 и int('0x144') вызывают ValueError. int(string, base) принимает основание системы счисления в качестве второго необязательного аргумента, поэтому int('0x144', 16) == 324. Если указать основание 0, число интерпретируется по правилам Python: ведущий '0' означает восьмеричное, а '0x' – шестнадцатеричное.
Не используйте встроенную функцию eval(), если вам нужно только преобразовать строки в числа. eval() будет значительно медленнее и представляет угрозу безопасности: кто-то может передать вам Python-выражение с нежелательными побочными эффектами. Например, кто-то может передать __import__('os').system("rm -rf $HOME"), что удалит ваш домашний каталог.
eval() также интерпретирует числа как Python-выражения, так что, например, eval('09') вызывает синтаксическую ошибку, поскольку Python не допускает ведущий ноль в десятичных числах (кроме самого '0').
Как преобразовать число в строку?¶How do I convert a number to a string?
Чтобы преобразовать, например, число 144 в строку '144', используйте встроенный конструктор типа str(). Если нужны шестнадцатеричное или восьмеричное представление, используйте встроенные функции hex() или oct(). Для расширенного форматирования см. раздел String Formatting, например, "{:04d}".format(144) даёт '0144', а "{:.3f}".format(1/3) даёт '0.333'.
Как изменить строку на месте?¶How do I modify a string in place?
Нельзя, потому что строки неизменяемы. В большинстве случаев следует просто собрать новую строку из нужных частей. Однако если нужен объект, способный изменять unicode-данные на месте, попробуйте использовать объект io.StringIO или модуль array:
>>> 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'
Как использовать строки для вызова функций/методов?¶How do I use strings to call functions/methods?
Существует несколько приёмов.
Лучший из них – использовать словарь, сопоставляющий строки с функциями. Главное преимущество этого подхода в том, что строки не обязаны совпадать с именами функций. Это также основной способ эмуляции конструкции case:
def a(): pass def b(): pass dispatch = {'go': a, 'stop': b} # Обратите внимание на отсутствие скобок для функций dispatch[get_input()]() # Обратите внимание на завершающие скобки для вызова функции
Используйте встроенную функцию getattr():
import foo getattr(foo, 'bar')()
Обратите внимание, что getattr() работает с любым объектом, включая классы, экземпляры классов, модули и т.д.
Это используется в нескольких местах стандартной библиотеки, например:
class Foo: def do_foo(self): ... def do_bar(self): ... f = getattr(foo_instance, 'do_' + opname) f()
Используйте locals() или eval() для разрешения имени функции:
def myFunc(): print("hello") fname = "myFunc" f = locals()[fname] f() f = eval(fname) f()
Примечание: использование eval() медленно и опасно. Если у вас нет полного контроля над содержимым строки, кто-то может передать строку, которая приведёт к выполнению произвольной функции.
Существует ли аналог функции chomp() из Perl для удаления завершающих символов новой строки из строк?¶Is there an equivalent to Perl’s chomp() for removing trailing newlines from strings?
Вы можете использовать S.rstrip("\r\n"), чтобы удалить все символы конца строки из конца строки S, не удаляя при этом другие пробельные символы в конце. Если строка S содержит несколько строк с несколькими пустыми строками в конце, будут удалены символы конца строки для всех пустых строк:
>>> lines = ("line 1 \r\n"
... "\r\n"
... "\r\n")
>>> lines.rstrip("\n\r")
'line 1 '
Так как это обычно требуется только при построчном чтении текста, использование S.rstrip() таким образом вполне эффективно.
Существует ли аналог функций scanf() или sscanf()?¶Is there a scanf() or sscanf() equivalent?
Прямого аналога нет.
Для простого разбора ввода обычно проще всего разбить строку на слова, разделённые пробелами, с помощью метода split() строковых объектов, а затем преобразовать десятичные строки в числа с помощью int() или float(). split() поддерживает необязательный параметр "sep", который полезен, если строка использует не пробелы в качестве разделителя.
Для более сложного разбора ввода регулярные выражения мощнее, чем sscanf() в C, и лучше подходят для этой задачи.
Что означает ошибка UnicodeDecodeError или UnicodeEncodeError?¶What does ‘UnicodeDecodeError’ or ‘UnicodeEncodeError’ error mean?
Смотрите Unicode HOWTO.
Производительность¶Performance
Моя программа слишком медленная. Как её ускорить?¶My program is too slow. How do I speed it up?
В целом это непростой вопрос. Во-первых, вот список того, что стоит помнить, прежде чем углубляться:
- Характеристики производительности различаются в разных реализациях Python. В этом FAQ основное внимание уделяется CPython.
- Поведение может различаться в разных операционных системах, особенно когда речь идёт о вводе-выводе или многопоточности.
- Следует всегда находить узкие места в программе перед попытками оптимизировать какой-либо код (см. модуль profile).
- Написание бенчмарк-скриптов позволит быстро итерировать при поиске улучшений (см. модуль timeit).
- Настоятельно рекомендуется иметь хорошее покрытие кода (с помощью модульного тестирования или любой другой методики) перед возможным внесением регрессий, скрытых в сложных оптимизациях.
Тем не менее, есть много приёмов для ускорения кода на Python. Вот некоторые общие принципы, которые сильно помогают достичь приемлемого уровня производительности:
- Ускорение алгоритмов (или замена на более быстрые) может принести гораздо большую выгоду, чем попытки рассыпать микрооптимизации по всему коду.
- Используйте правильные структуры данных. Изучите документацию по Встроенным типам и модулю collections.
- Когда стандартная библиотека предоставляет примитив для выполнения какой-либо задачи, он, скорее всего (хотя и не гарантированно), будет быстрее любого другого решения, которое вы можете придумать. Это вдвойне верно для примитивов, написанных на C, таких как встроенные функции и некоторые типы расширений. Например, обязательно используйте либо встроенный метод list.sort(), либо связанную функцию sorted() для сортировки (и ознакомьтесь с mini-HOWTO по сортировке для примеров умеренно продвинутого использования).
- Абстракции обычно создают косвенность и заставляют интерпретатор работать больше. Если уровни косвенности перевешивают объём полезной работы, программа будет медленнее. Следует избегать чрезмерной абстракции, особенно в виде крошечных функций или методов (которые также часто вредят читаемости).
Если вы достигли предела возможностей чистого Python, существуют инструменты, которые помогут пойти дальше. Например, Cython может скомпилировать слегка изменённую версию кода Python в расширение C и может использоваться на многих разных платформах. Cython может использовать компиляцию (и необязательные аннотации типов), чтобы сделать ваш код значительно быстрее, чем при интерпретации. Если вы уверены в своих навыках программирования на C, вы также можете самостоятельно написать модуль расширения на C.
См. также
Вики-страница, посвящённая советам по производительности.
Как наиболее эффективно объединить много строк?¶What is the most efficient way to concatenate many strings together?
Объекты str и bytes являются неизменяемыми, поэтому конкатенация многих строк неэффективна, так как каждая конкатенация создаёт новый объект. В общем случае общая стоимость выполнения квадратично зависит от общей длины строк.
Чтобы накопить много объектов str, рекомендуется поместить их в список и в конце вызвать str.join():
chunks = []
for s in my_strings:
chunks.append(s)
result = ''.join(chunks)
(другой достаточно эффективный приём – использовать io.StringIO)
Чтобы накопить много объектов bytes, рекомендуется расширять объект bytearray с помощью конкатенации на месте (оператор +=):
result = bytearray()
for b in my_bytes_objects:
result += b
Последовательности (кортежи/списки)¶Sequences (Tuples/Lists)
Как преобразовывать кортежи и списки друг в друга?¶How do I convert between tuples and lists?
Конструктор типа tuple(seq) преобразует любую последовательность (на самом деле, любой итерируемый объект) в кортеж с теми же элементами в том же порядке.
Например, tuple([1, 2, 3]) даёт (1, 2, 3), а tuple('abc') даёт ('a', 'b', 'c'). Если аргумент уже является кортежем, то копия не создаётся, а возвращается тот же объект, поэтому вызов tuple() дёшев, если не уверены, что объект уже кортеж.
Конструктор типа list(seq) преобразует любую последовательность или итерируемый объект в список с теми же элементами в том же порядке. Например, list((1, 2, 3)) даёт [1, 2, 3], а list('abc') даёт ['a', 'b', 'c']. Если аргумент уже список, то создаётся копия, как и в случае seq[:].
Что такое отрицательный индекс?¶What’s a negative index?
Последовательности Python индексируются положительными и отрицательными числами. Для положительных чисел 0 – это первый индекс, 1 – второй и т.д. Для отрицательных индексов -1 – это последний, -2 – предпоследний и т.д. Считайте, что seq[-n] эквивалентно seq[len(seq)-n].
Использование отрицательных индексов может быть очень удобным. Например, S[:-1] – это вся строка, кроме последнего символа, что полезно для удаления завершающего символа новой строки.
Как перебрать последовательность в обратном порядке?¶How do I iterate over a sequence in reverse order?
Используйте встроенную функцию reversed(), которая появилась в Python 2.4:
for x in reversed(sequence):
... # выполнить действие с x...
Это не затронет исходную последовательность, а создаст новую копию с обратным порядком для итерации.
В Python 2.3 можно использовать расширенный синтаксис срезов:
for x in sequence[::-1]:
... # выполнить действие с x...
Как удалить дубликаты из списка?¶How do you remove duplicates from a list?
См. Python Cookbook, где подробно рассматриваются многие способы сделать это:
Если перестановка элементов списка не страшна, отсортируйте его, а затем просмотрите с конца, удаляя дубликаты по ходу:
if mylist:
mylist.sort()
last = mylist[-1]
for i in range(len(mylist)-2, -1, -1):
if last == mylist[i]:
del mylist[i]
else:
last = mylist[i]
Если все элементы списка могут использоваться как ключи множества (т.е. все они хешируемы), это часто быстрее
mylist = list(set(mylist))
Это преобразует список во множество, удаляя дубликаты, а затем обратно в список.
Как создать массив в Python?¶How do you make an array in Python?
Используйте список:
["this", 1, "is", "an", "array"]
По временно́й сложности списки эквивалентны массивам C или Pascal; основное отличие в том, что список Python может содержать объекты разных типов.
Модуль array также предоставляет методы для создания массивов фиксированных типов с компактным представлением, но они медленнее индексируются, чем списки. Также обратите внимание, что расширения Numeric и другие определяют структуры, похожие на массивы, с различными характеристиками.
Чтобы получить связные списки в стиле Lisp, можно эмулировать ячейки cons с помощью кортежей:
lisp_list = ("like", ("this", ("example", None) ) )
Если нужна изменяемость, можно использовать списки вместо кортежей. Здесь аналог lisp car – это lisp_list[0], а аналог cdr – lisp_list[1]. Делайте так, только если уверены, что это действительно необходимо, потому что обычно это намного медленнее, чем использование списков Python.
Как создать многомерный список?¶How do I create a multidimensional list?
Вероятно, вы пытались создать многомерный массив так:
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
Это порождает список, содержащий 3 разных списка длины два. Также можно использовать списковое включение:
w, h = 2, 3
A = [[None] * w for i in range(h)]
Или можно использовать расширение, предоставляющее тип данных матрица; наиболее известно Numeric Python.
Как применить метод к последовательности объектов?¶How do I apply a method to a sequence of objects?
Используйте списковое включение:
result = [obj.method() for obj in mylist]
Словари¶Dictionaries
Как сделать так, чтобы ключи словаря отображались в постоянном порядке?¶How can I get a dictionary to display its keys in a consistent order?
Невозможно. Словари хранят ключи в непредсказуемом порядке, поэтому порядок отображения элементов словаря будет таким же непредсказуемым.
Это может раздражать, если вы хотите сохранить версию для печати в файл, внести некоторые изменения, а затем сравнить её с другим распечатанным словарём. В этом случае используйте модуль pprint для форматированного вывода словаря; элементы будут представлены в порядке, отсортированном по ключу.
Более сложное решение – создать подкласс dict для создания класса SortedDict, который выводит себя в предсказуемом порядке. Вот одна простоватая реализация такого класса:
class SortedDict(dict):
def __repr__(self):
keys = sorted(self.keys())
result = ("{!r}: {!r}".format(k, self[k]) for k in keys)
return "{{{}}}".format(", ".join(result))
__str__ = __repr__
Это будет работать для многих распространённых ситуаций, которые могут встретиться, хотя это далеко не идеальное решение. Самый большой недостаток: если некоторые значения в словаре сами являются словарями, их значения не будут представлены в каком-либо определённом порядке.
Я хочу выполнить сложную сортировку: можно ли сделать преобразование Шварца в Python?¶I want to do a complicated sort: can you do a Schwartzian Transform in Python?
Этот метод, приписываемый Рэндалу Шварцу из сообщества Perl, сортирует элементы списка по метрике, которая отображает каждый элемент в его "значение сортировки". В Python просто используйте аргумент key для метода sort():
Isorted = L[:]
Isorted.sort(key=lambda s: int(s[10:15]))
Аргумент key появился в Python 2.4, для более старых версий такую сортировку довольно просто выполнить с помощью списковых включений. Чтобы отсортировать список строк по их значениям в верхнем регистре:
tmp1 = [(x.upper(), x) for x in L] # Преобразование Шварца
tmp1.sort()
Usorted = [x[1] for x in tmp1]
Чтобы отсортировать по целочисленному значению подполя, расположенного с 10 по 15 позицию в каждой строке:
tmp2 = [(int(s[10:15]), s) for s in L] # Преобразование Шварца
tmp2.sort()
Isorted = [x[1] for x in tmp2]
Для версий до 3.0, Isorted также можно вычислить с помощью
def intfield(s):
return int(s[10:15])
def Icmp(s1, s2):
return cmp(intfield(s1), intfield(s2))
Isorted = L[:]
Isorted.sort(Icmp)
но поскольку этот метод вызывает intfield() много раз для каждого элемента L, он медленнее преобразования Шварца.
Как отсортировать один список по значениям из другого списка?¶How can I sort one list by values from another list?
Объедините их в итератор кортежей, отсортируйте полученный список, а затем извлеките нужный элемент.
>>> list1 = ["what", "I'm", "sorting", "by"]
>>> list2 = ["something", "else", "to", "sort"]
>>> pairs = zip(list1, list2)
>>> pairs = sorted(pairs)
>>> pairs
[("I'm", 'else'), ('by', 'sort'), ('sorting', 'to'), ('what', 'something')]
>>> result = [x[1] for x in pairs]
>>> result
['else', 'sort', 'to', 'something']
Альтернатива для последнего шага:
>>> result = []
>>> for p in pairs: result.append(p[1])
Если такой вариант кажется вам более читаемым, возможно, вы предпочтёте его итоговому списковому включению. Однако для длинных списков он почти вдвое медленнее. Почему? Во-первых, операция append() требует перераспределения памяти, и хотя для этого используются некоторые уловки, чтобы избегать его каждый раз, время от времени это всё же происходит, и это обходится довольно дорого. Во-вторых, выражение «result.append» требует дополнительного поиска атрибута. В-третьих, из-за необходимости совершать все эти вызовы функций скорость снижается.
Объекты¶Objects
Что такое класс?¶What is a class?
Класс – это конкретный тип объекта, создаваемый при выполнении инструкции class. Объекты классов используются как шаблоны для создания объектов-экземпляров, которые содержат как данные (атрибуты), так и код (методы), характерные для данного типа данных.
Класс может быть основан на одном или нескольких других классах, называемых его базовыми классами. Он наследует атрибуты и методы своих базовых классов. Это позволяет последовательно уточнять объектную модель с помощью наследования. У вас может быть общий класс Mailbox, предоставляющий базовые методы доступа для почтового ящика, и подклассы, такие как MboxMailbox, MaildirMailbox, OutlookMailbox, которые работают с различными конкретными форматами почтовых ящиков.
Что такое метод?¶What is a method?
Метод – это функция некоторого объекта x, которую обычно вызывают как x.name(аргументы...). Методы определяются как функции внутри определения класса:
class C:
def meth (self, arg):
return arg * 2 + self.attribute
Что такое self?¶What is self?
Self – это всего лишь общепринятое имя для первого аргумента метода. Метод, определённый как meth(self, a, b, c), следует вызывать как x.meth(a, b, c) для некоторого экземпляра x класса, в котором находится определение; вызываемый метод будет считать, что его вызвали как meth(x, a, b, c).
См. также Почему self должно явно использоваться в определениях и вызовах методов?.
Как проверить, является ли объект экземпляром данного класса или его подкласса?¶How do I check if an object is an instance of a given class or of a subclass of it?
Используйте встроенную функцию isinstance(obj, cls). Можно проверить, является ли объект экземпляром любого из нескольких классов, передав кортеж вместо одного класса, например isinstance(obj, (class1, class2, ...)), а также можно проверить, является ли объект одним из встроенных типов Python, например isinstance(obj, str) или isinstance(obj, (int, float, complex)).
Обратите внимание: в большинстве программ isinstance() для пользовательских классов используется нечасто. Если вы сами разрабатываете классы, более правильным объектно-ориентированным стилем будет определять в классах методы, инкапсулирующие определённое поведение, вместо проверки класса объекта и выполнения разных действий в зависимости от того, к какому классу он принадлежит. Например, если у вас есть функция, которая делает что-то:
def search(obj):
if isinstance(obj, Mailbox):
# ... код для поиска в почтовом ящике
elif isinstance(obj, Document):
# ... код для поиска в документе
elif ...
Более правильный подход – определить метод search() во всех классах и просто вызывать его:
class Mailbox:
def search(self):
# ... код для поиска в почтовом ящике
class Document:
def search(self):
# ... код для поиска в документе
obj.search()
Что такое делегирование?¶What is delegation?
Делегирование – это объектно-ориентированный приём (также называемый шаблоном проектирования). Допустим, у вас есть объект x, и вы хотите изменить поведение только одного из его методов. Можно создать новый класс, который предоставляет новую реализацию интересующего вас метода, а все остальные методы делегирует соответствующим методам объекта x.
Программисты на Python могут легко реализовать делегирование. Например, следующий класс реализует класс, который ведёт себя как файл, но преобразует все записываемые данные в верхний регистр:
class UpperOut:
def __init__(self, outfile):
self._outfile = outfile
def write(self, s):
self._outfile.write(s.upper())
def __getattr__(self, name):
return getattr(self._outfile, name)
Здесь класс UpperOut переопределяет метод write(), чтобы преобразовывать строку аргумента в верхний регистр перед вызовом базового метода self.__outfile.write(). Все остальные методы делегируются базовому объекту self.__outfile. Делегирование осуществляется через метод __getattr__; обратитесь к ссылке на язык для получения дополнительной информации об управлении доступом к атрибутам.
Обратите внимание: в более общих случаях делегирование может усложняться. Когда атрибуты необходимо не только получать, но и устанавливать, класс должен также определить метод __setattr__(), и делать это нужно аккуратно. Базовая реализация __setattr__() примерно эквивалентна следующему:
class X:
...
def __setattr__(self, name, value):
self.__dict__[name] = value
...
Большинство реализаций __setattr__() должны изменять self.__dict__ для хранения локального состояния self, чтобы не вызывать бесконечную рекурсию.
Как вызвать метод, определённый в базовом классе, из производного класса, который его переопределяет?¶How do I call a method defined in a base class from a derived class that overrides it?
Используйте встроенную функцию super():
class Derived(Base):
def meth (self):
super(Derived, self).meth()
Для версий до 3.0 вы могли использовать классические классы. Для определения класса вида class Derived(Base): ... можно вызвать метод meth(), определённый в Base (или одном из базовых классов Base), как Base.meth(self, аргументы...). Здесь Base.meth – это несвязанный метод, поэтому нужно передать аргумент self.
Как организовать код, чтобы было проще изменить базовый класс?¶How can I organize my code to make it easier to change the base class?
Можно определить псевдоним для базового класса, присвоить ему реальный базовый класс до определения вашего класса и использовать псевдоним во всём классе. Тогда всё, что нужно изменить – это значение, присвоенное псевдониму. Кстати, этот трюк также удобен, если вы хотите динамически (например, в зависимости от доступности ресурсов) выбирать, какой базовый класс использовать. Пример:
BaseAlias = <real base class>
class Derived(BaseAlias):
def meth(self):
BaseAlias.meth(self)
...
Как создать статические данные класса и статические методы класса?¶How do I create static class data and static class methods?
В Python поддерживаются как статические данные, так и статические методы (в смысле C++ или Java).
Для статических данных просто определите атрибут класса. Чтобы присвоить атрибуту новое значение, необходимо явно указать имя класса в присваивании:
class C:
count = 0 # количество вызовов C.__init__
def __init__(self):
C.count = C.count + 1
def getcount(self):
return C.count # или вернуть self.count
c.count также ссылается на C.count для любого c, для которого выполняется isinstance(c, C), если только это не переопределено самим объектом c или каким-либо классом на пути поиска от c.__class__ обратно к C.
Предупреждение: внутри метода C присваивание вида self.count = 42 создаёт новый, не связанный экземпляр с именем «count» в собственном словаре self. Повторное связывание статического имени данных класса всегда должно указывать сам класс, независимо от того, находится ли оно внутри метода или нет:
C.count = 314
Статические методы возможны:
class C:
@staticmethod
def static(arg1, arg2, arg3):
# Нет параметра 'self'!
...
Однако гораздо более прямолинейный способ получить эффект статического метода – это простая функция уровня модуля:
def getcount():
return C.count
Если код организован так, что на каждый модуль определён один класс (или тесно связанная иерархия классов), это обеспечивает нужную инкапсуляцию.
Как перегрузить конструкторы (или методы) в Python?¶How can I overload constructors (or methods) in Python?
Этот ответ на самом деле применим ко всем методам, но вопрос обычно возникает в первую очередь в контексте конструкторов.
В C++ вы бы написали
class C {
C() { cout << "No arguments\n"; }
C(int i) { cout << "Argument is " << i << "\n"; }
}
В Python нужно написать один конструктор, который обрабатывает все случаи с помощью аргументов по умолчанию. Например:
class C:
def __init__(self, i=None):
if i is None:
print("No arguments")
else:
print("Argument is", i)
Это не полностью эквивалентно, но на практике достаточно близко.
Можно также попробовать список аргументов переменной длины, например
def __init__(self, *args):
...
Тот же подход работает для всех определений методов.
Я пытаюсь использовать __spam и получаю ошибку про _SomeClassName__spam.¶I try to use __spam and I get an error about _SomeClassName__spam.
Имена переменных с двойным подчёркиванием в начале подвергаются «искажению», что даёт простой, но эффективный способ определения частных переменных класса. Любой идентификатор вида __spam (не менее двух ведущих подчёркиваний, не более одного завершающего) текстуально заменяется на _classname__spam, где classname – это имя текущего класса с удалёнными ведущими подчёркиваниями.
Это не гарантирует приватности: внешний пользователь всё равно может намеренно получить доступ к атрибуту «_classname__spam», а частные значения видны в __dict__ объекта. Многие программисты Python вообще не утруждают себя использованием частных имён переменных.
Мой класс определяет __del__, но он не вызывается, когда я удаляю объект.¶My class defines __del__ but it is not called when I delete the object.
Для этого есть несколько возможных причин.
Оператор del не обязательно вызывает __del__() – он просто уменьшает счётчик ссылок объекта, и если тот достигает нуля, вызывается __del__().
Если ваши структуры данных содержат циклические ссылки (например, дерево, где каждый потомок имеет ссылку на родителя, а каждый родитель – список потомков), счётчики ссылок никогда не обнулятся. Время от времени Python запускает алгоритм для обнаружения таких циклов, но сборщик мусора может запуститься спустя некоторое время после исчезновения последней ссылки на вашу структуру данных, поэтому ваш метод __del__() может быть вызван в неудобный и случайный момент. Это неудобно, если вы пытаетесь воспроизвести проблему. Хуже того, порядок, в котором выполняются методы __del__() объектов, произволен. Вы можете запустить gc.collect(), чтобы принудительно выполнить сборку, но существуют патологические случаи, когда объекты никогда не будут собраны.
Несмотря на сборщик циклов, всё же стоит определять явный метод close() для объектов, который следует вызывать, когда объект больше не нужен. Метод close() может затем удалять атрибуты, ссылающиеся на подобъекты. Не вызывайте __del__() напрямую – __del__() должен вызывать close(), а close() должен гарантировать, что его можно вызвать для одного и того же объекта несколько раз.
Ещё один способ избежать циклических ссылок – использовать модуль weakref, который позволяет указывать на объекты без увеличения их счётчика ссылок. Например, в древовидных структурах данных для ссылок на родителя и соседей (если они нужны) следует использовать слабые ссылки.
Наконец, если ваш метод __del__() вызывает исключение, в sys.stderr выводится предупреждение.
Как получить список всех экземпляров данного класса?¶How do I get a list of all instances of a given class?
Python не отслеживает все экземпляры класса (или встроенного типа). Можно запрограммировать конструктор класса так, чтобы он отслеживал все экземпляры, сохраняя список слабых ссылок на каждый экземпляр.
Модули¶Modules
Как создать .pyc-файл?¶How do I create a .pyc file?
Когда модуль импортируется впервые (или когда исходный код новее текущего скомпилированного файла), в том же каталоге, что и файл .py, должен быть создан файл .pyc, содержащий скомпилированный код.
Одна из причин, по которой файл .pyc может не создаваться, – проблемы с правами доступа к каталогу. Это может произойти, например, если вы разрабатываете как один пользователь, а запускаете как другой, например, при тестировании с веб-сервером. Создание файла .pyc происходит автоматически, если вы импортируете модуль и у Python есть возможность (права, свободное место и т.д.) записать скомпилированный модуль обратно в каталог.
Запуск Python на верхнеуровневом скрипте не считается импортом, и файл .pyc создан не будет. Например, если у вас есть модуль верхнего уровня abc.py, который импортирует другой модуль xyz.py, то при запуске abc будет создан xyz.pyc, так как xyz импортируется, но файл abc.pyc создан не будет, поскольку abc.py не импортируется.
Если необходимо создать abc.pyc – то есть создать .pyc-файл для модуля, который не импортируется – это можно сделать с помощью модулей py_compile и compileall.
Модуль py_compile может вручную компилировать любой модуль. Один из способов – интерактивно использовать функцию compile() из этого модуля:
>>> import py_compile
>>> py_compile.compile('abc.py')
Это запишет файл .pyc в то же расположение, что и abc.py (или вы можете переопределить это с помощью необязательного параметра cfile).
Вы также можете автоматически скомпилировать все файлы в каталоге или каталогах, используя модуль compileall. Это можно сделать из командной строки, запустив compileall.py и указав путь к каталогу, содержащему файлы Python для компиляции:
python -m compileall .
Как узнать имя текущего модуля?¶How do I find the current module name?
Модуль может узнать своё собственное имя, обратившись к предопределённой глобальной переменной __name__. Если она равна '__main__', программа выполняется как сценарий. Многие модули, которые обычно используются путём импорта, также предоставляют интерфейс командной строки или самопроверку и выполняют этот код только после проверки __name__:
def main():
print('Running test...')
...
if __name__ == '__main__':
main()
Как сделать модули, которые импортируют друг друга?¶How can I have modules that mutually import each other?
Предположим, у вас есть следующие модули:
foo.py:
from bar import bar_var
foo_var = 1
bar.py:
from foo import foo_var
bar_var = 2
Проблема в том, что интерпретатор выполнит следующие шаги:
- main импортирует foo
- Создаются пустые глобальные переменные для foo
- foo компилируется и начинает выполняться
- foo импортирует bar
- Создаются пустые глобальные переменные для bar
- bar компилируется и начинает выполняться
- bar импортирует foo (это пустая операция, так как модуль с именем foo уже существует)
- bar.foo_var = foo.foo_var
Последний шаг завершается ошибкой, потому что Python ещё не закончил интерпретацию foo, и глобальный словарь символов для foo всё ещё пуст.
То же самое происходит при использовании import foo и последующей попытке обратиться к foo.foo_var в глобальном коде.
Существует (как минимум) три возможных обходных решения этой проблемы.
Гвидо ван Россум рекомендует избегать всех случаев использования from <module> import ... и размещать весь код внутри функций. Инициализации глобальных переменных и переменных класса должны использовать только константы или встроенные функции. Это означает, что всё из импортированного модуля указывается как <module>.<name>.
Джим Роскинд предлагает выполнять шаги в следующем порядке в каждом модуле:
- экспорт (глобальные переменные, функции и классы, которым не нужны импортированные базовые классы)
- Операторы import
- активный код (включая глобальные переменные, инициализируемые из импортированных значений).
ван Россуму такой подход не очень нравится, потому что импорты оказываются в странном месте, но это работает.
Маттиас Урлихс рекомендует перестроить код так, чтобы рекурсивный импорт вообще не требовался.
Эти решения не являются взаимоисключающими.
__import__(‘x.y.z’) возвращает <module ‘x’>; как получить z?¶__import__(‘x.y.z’) returns <module ‘x’>; how do I get z?
Попробуйте:
__import__('x.y.z').y.z
В более реалистичных ситуациях, возможно, придётся сделать что-то вроде
m = __import__(s)
for i in s.split(".")[1:]:
m = getattr(m, i)
См. importlib для удобной функции import_module().
Когда я редактирую импортированный модуль и повторно импортирую его, изменения не отображаются. Почему это происходит?¶When I edit an imported module and reimport it, the changes don’t show up. Why does this happen?
Из соображений эффективности и согласованности Python читает файл модуля только при первом импорте модуля. Если бы это было не так, в программе, состоящей из многих модулей, каждый из которых импортирует один и тот же базовый модуль, базовый модуль разбирался бы и переразбирался многократно. Чтобы принудительно перечитать изменённый модуль, сделайте следующее:
import imp
import modname
imp.reload(modname)
Предупреждение: этот метод не является абсолютно надёжным. В частности, модули, содержащие операторы типа
from modname import some_objects
будут продолжать работать со старой версией импортированных объектов. Если модуль содержит определения классов, существующие экземпляры классов не будут обновлены для использования нового определения класса. Это может привести к следующему парадоксальному поведению:
>>> import imp
>>> import cls
>>> c = cls.C() # Создать экземпляр C
>>> imp.reload(cls)
<module 'cls' from 'cls.py'>
>>> isinstance(c, cls.C) # isinstance ложно?!?
False
Суть проблемы становится ясна, если вывести «идентичность» объектов классов:
>>> hex(id(c.__class__))
'0x7352a0'
>>> hex(id(cls.C))
'0x4198d0'