Pemrograman FAQ¶
Pertanyaan Umum¶
Czy istnieje debugger kodu źródłowego z obsługą punktów przerwania, krokowym wykonywaniem akcji itp.?¶
Ya.
Нижче описано кілька налагоджувачів для Python, і вбудована функція breakpoint()
дозволяє вам перейти до будь-якого з них.
Модуль pdb — це простий, але адекватний налагоджувач у консольному режимі для Python. Це частина стандартної бібліотеки Python і задокументована в Library Reference Manual
. Ви також можете написати власний налагоджувач, використовуючи код для pdb як приклад.
The IDLE interactive development environment, which is part of the standard Python distribution (normally available as Tools/scripts/idle3), includes a graphical debugger.
PythonWin — це середовище розробки Python, яке містить налагоджувач графічного інтерфейсу на основі pdb. Налагоджувач PythonWin забарвлює точки зупину та має чимало цікавих функцій, таких як налагодження програм, які не належать до PythonWin. PythonWin доступний як частина проекту pywin32 і як частина дистрибутива ActivePython.
Eric is an IDE built on PyQt and the Scintilla editing component.
trepan3k є gdb-подібним налагоджувачем.
Visual Studio Code — це середовище розробки з інструментами налагодження, яке інтегрується з програмним забезпеченням для керування версіями.
Ada sejumlah IDE Python komersial yang menyertakan debugger berbentuk grafis. Mereka adalah:
Apakah terdapat alat untuk membantu menemukan bug atau melakukan analisis yang bersifat statis?¶
Ya.
Pylint and Pyflakes do basic checking that will help you catch bugs sooner.
Static type checkers such as Mypy, Pyre, and Pytype can check type hints in Python source code.
Як я можу створити автономний двійковий файл зі сценарію Python?¶
Вам не потрібна можливість компілювати код Python до C, якщо все, що вам потрібно, це окрема програма, яку користувачі можуть завантажити та запустити без попередньої інсталяції дистрибутива Python. Існує ряд інструментів, які визначають набір модулів, необхідних для програми, і зв’язують ці модулі з двійковим файлом Python для створення єдиного виконуваного файлу.
One is to use the freeze tool, which is included in the Python source tree as Tools/freeze. It converts Python byte code to C arrays; with a C compiler you can embed all your modules into a new program, which is then linked with the standard Python modules.
Він працює шляхом рекурсивного сканування джерела на наявність інструкцій імпорту (в обох формах) і пошуку модулів у стандартному шляху Python, а також у вихідному каталозі (для вбудованих модулів). Потім він перетворює байт-код для модулів, написаних на Python, у код C (ініціалізатори масивів, які можна перетворити на об’єкти коду за допомогою модуля marshal) і створює спеціальний файл конфігурації, який містить лише ті вбудовані модулі, які фактично використовуються в програма. Потім він компілює згенерований код C і пов’язує його з рештою інтерпретатора Python, щоб сформувати самодостатній двійковий файл, який діє точно так само, як ваш сценарій.
Наступні пакети можуть допомогти зі створенням консолі та виконуваних файлів графічного інтерфейсу користувача:
Nuitka (Кросплатформенний)
PyInstaller (Cross-platform)
PyOxidizer (Кросплатформенний)
cx_Freeze (Кросплатформенний)
py2app (лише для macOS)
py2exe (Windows only)
Чи існують стандарти кодування чи керівництво по стилю для програм Python?¶
Так. Стиль кодування, необхідний для модулів стандартної бібліотеки, задокументовано як PEP 8.
Inti Bahasa¶
Чому я отримую помилку UnboundLocalError, коли змінна має значення?¶
It can be a surprise to get the UnboundLocalError
in previously working
code when it is modified by adding an assignment statement somewhere in
the body of a function.
Kode ini:
>>> x = 10
>>> def bar():
... print(x)
...
>>> bar()
10
dapat beroperasi, tapi kode ini:
>>> x = 10
>>> def foo():
... print(x)
... x += 1
results in an 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
Apa saja aturan-aturan untuk variabel lokal dan global di Python?¶
У Python змінні, на які посилаються лише всередині функції, є неявно глобальними. Якщо змінній присвоєно значення будь-де в тілі функції, вона вважається локальною, якщо вона явно не оголошена як глобальна.
Хоча це трохи дивно спочатку, мить міркування пояснює це. З одного боку, вимога global
для призначених змінних забезпечує захист від ненавмисних побічних ефектів. З іншого боку, якби global
був потрібний для всіх глобальних посилань, ви б використовували global
весь час. Ви повинні оголосити як глобальне кожне посилання на вбудовану функцію або на компонент імпортованого модуля. Цей безлад перекреслив би корисність оголошення global
для визначення побічних ефектів.
Mengapa lambda yang didefinisikan dalam sebuah perulangan dengan nilai yang berbeda semuanya mengembalikan hasil yang sama?¶
Припустімо, що ви використовуєте цикл 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
Зауважте, що ця поведінка не властива лямбда-виразам, але також стосується звичайних функцій.
Які "найкращі практики" використання імпорту в модулі?¶
Загалом, не використовуйте from modulename import *
. Це захаращує простір імен імпортера, і значно ускладнює для лінтерів виявлення невизначених імен.
Імпорт модулів у верхній частині файлу. Це дає зрозуміти, які інші модулі вимагає ваш код, і уникає запитань про те, чи входить ім’я модуля в область видимості. Використання одного імпорту на рядок полегшує додавання та видалення імпортованих модулів, але використання кількох імпортів на рядок займає менше місця на екрані.
Доцільно імпортувати модулі в такому порядку:
third-party library modules (anything installed in Python's site-packages directory) -- e.g.
dateutil
,requests
,PIL.Image
locally developed modules
Іноді необхідно перемістити імпорт до функції чи класу, щоб уникнути проблем із циклічним імпортом. Гордон Макміллан каже:
Циклічний імпорт підходить, якщо обидва модулі використовують форму імпорту "import <module>". Вони зазнають невдачі, коли 2-й модуль хоче отримати ім’я з першого ("з імені імпорту модуля"), а імпорт здійснюється на верхньому рівні. Це тому, що імена в 1-му ще недоступні, оскільки перший модуль зайнятий імпортом 2-го.
У цьому випадку, якщо другий модуль використовується лише в одній функції, імпорт можна легко перемістити в цю функцію. До моменту виклику імпорту перший модуль завершить ініціалізацію, і другий модуль зможе виконувати свій імпорт.
Також може знадобитися перемістити імпорт із верхнього рівня коду, якщо деякі модулі залежать від платформи. У цьому випадку може бути навіть неможливо імпортувати всі модулі у верхній частині файлу. У цьому випадку хорошим варіантом є імпорт правильних модулів у відповідний специфічний для платформи код.
Переміщуйте імпорт у локальну область, наприклад, у визначення функції, лише якщо це необхідно для вирішення проблеми, як-от уникнення циклічного імпорту або намагання скоротити час ініціалізації модуля. Ця техніка особливо корисна, якщо багато імпортів непотрібні залежно від того, як виконується програма. Ви також можете перемістити імпорт у функцію, якщо модулі використовуються лише в цій функції. Зауважте, що завантаження модуля вперше може бути дорогим через одноразову ініціалізацію модуля, але багаторазове завантаження модуля практично безкоштовне, коштуючи лише кількох пошуків у словнику. Навіть якщо назва модуля вийшла за межі видимості, модуль, імовірно, доступний у sys.modules
.
Як я можу передати необов’язкові або ключові параметри з однієї функції в іншу?¶
Зберіть аргументи за допомогою специфікаторів *
і **
у списку параметрів функції; це дає вам позиційні аргументи як кортеж і ключові аргументи як словник. Потім ви можете передати ці аргументи під час виклику іншої функції за допомогою *
і **
:
def f(x, *args, **kwargs):
...
kwargs['width'] = '14.3c'
...
g(x, *args, **kwargs)
Jaka jest różnica pomiędzy argumentami a parametrami?¶
Parameters are defined by the names that appear in a function definition, whereas arguments are the values actually passed to a function when calling it. Parameters define what kind of arguments a function can accept. For example, given the function definition:
def func(foo, bar=None, **kwargs):
pass
foo, bar і kwargs є параметрами func
. Однак під час виклику func
, наприклад:
func(42, bar=314, extra=somevar)
значення 42
, 314
і somevar
є аргументами.
Чому зміна списку 'y' також змінила список 'x'?¶
Jika kamu menulis kode seperti:
>>> x = []
>>> y = x
>>> y.append(10)
>>> y
[10]
>>> x
[10]
можливо, вам буде цікаво, чому додавання елемента до y
також змінило x
.
Terdapat dua faktor yang menghasilkan hasil ini:
Змінні - це просто імена, які посилаються на об'єкти. Виконання
y = x
не створює копію списку — воно створює нову зміннуy
, яка посилається на той самий об’єкт, на який посилаєтьсяx
. Це означає, що є лише один об’єкт (список), і якx
, так іy
посилаються на нього.Списки mutable, що означає, що ви можете змінювати їхній вміст.
After the call to append()
, the content of the mutable object has
changed from []
to [10]
. Since both the variables refer to the same
object, using either name accesses the modified value [10]
.
Якщо натомість ми призначимо незмінний об’єкт x
:
>>> x = 5 # ints are immutable
>>> y = x
>>> x = x + 1 # 5 can't be mutated, we are creating a new object here
>>> x
6
>>> y
5
ми бачимо, що в цьому випадку x
і y
більше не рівні. Це тому, що цілі числа immutable, і коли ми робимо x = x + 1
, ми не змінюємо int 5
, збільшуючи його значення; натомість ми створюємо новий об’єкт (int 6
) і призначаємо його x
(тобто змінюємо, на який об’єкт посилається x
). Після цього призначення ми маємо два об’єкти (цілі 6
і 5
) і дві змінні, які посилаються на них (x
тепер посилається на 6
, але y
все ще посилається на 5
).
Some operations (for example y.append(10)
and y.sort()
) mutate the
object, whereas superficially similar operations (for example y = y + [10]
and sorted(y)
) create a new object. In general in Python (and in all cases
in the standard library) a method that mutates an object will return None
to help avoid getting the two types of operations confused. So if you
mistakenly write y.sort()
thinking it will give you a sorted copy of y
,
you'll instead end up with None
, which will likely cause your program to
generate an easily diagnosed error.
Однак існує один клас операцій, де одна й та сама операція іноді має різну поведінку з різними типами: розширені оператори присвоєння. Наприклад, +=
змінює списки, але не кортежі чи цілі (a_list += [1, 2, 3]
еквівалентно a_list.extend([1, 2, 3])
і змінює a_list
, тоді як some_tuple += (1, 2, 3)
і some_int += 1
створюють нові об’єкти).
Dengan kata lain:
Якщо у нас є змінний об’єкт (
list
,dict
,set
тощо), ми можемо використати певні операції, щоб змінити його, і всі змінні, які посилаються на нього, будуть побачити зміни.Якщо у нас є незмінний об’єкт (
str
,int
,tuple
тощо), усі змінні, які посилаються на нього, завжди бачитимуть те саме значення, але операції, що перетворюють це значення в нове значення завжди повертає новий об’єкт.
Якщо ви хочете знати, чи дві змінні посилаються на той самий об’єкт, ви можете скористатися оператором is
або вбудованою функцією id()
.
Як написати функцію з вихідними параметрами (виклик за посиланням)?¶
Пам’ятайте, що в Python аргументи передаються шляхом присвоєння. Оскільки присвоєння лише створює посилання на об’єкти, немає псевдоніма між ім’ям аргументу у викликаючому та викликаному, а тому немає виклику за посиланням як такого. Бажаного ефекту можна досягти кількома способами.
Повертаючи кортеж результатів:
>>> def func1(a, b): ... a = 'new-value' # a and b are local names ... b = b + 1 # assigned to new objects ... return a, b # return new values ... >>> x, y = 'old-value', 99 >>> func1(x, y) ('new-value', 100)
Ini merupakan solusi yang jelas.
За допомогою глобальних змінних. Це небезпечно для потоків і не рекомендується.
Передаючи змінний (змінний на місці) об’єкт:
>>> def func2(a): ... a[0] = 'new-value' # 'a' references a mutable list ... a[1] = a[1] + 1 # changes a shared object ... >>> args = ['old-value', 99] >>> func2(args) >>> args ['new-value', 100]
Передаючи словник, який мутується:
>>> def func3(args): ... args['a'] = 'new-value' # args is a mutable dictionary ... args['b'] = args['b'] + 1 # change it in-place ... >>> args = {'a': 'old-value', 'b': 99} >>> func3(args) >>> args {'a': 'new-value', 'b': 100}
Або об'єднайте значення в екземпляр класу:
>>> class Namespace: ... def __init__(self, /, **args): ... for key, value in args.items(): ... setattr(self, key, value) ... >>> def func4(args): ... args.a = 'new-value' # args is a mutable Namespace ... args.b = args.b + 1 # change object in-place ... >>> args = Namespace(a='old-value', b=99) >>> func4(args) >>> vars(args) {'a': 'new-value', 'b': 100}
Майже ніколи не буває вагомих причин ускладнювати це.
Ваш найкращий вибір — повернути кортеж, що містить кілька результатів.
Як створити функцію вищого порядку в 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
Dalam kedua kasus,
taxes = linear(0.3, 2)
дає викликуваний об’єкт, де taxes(10e6) == 0,3 * 10e6 + 2
.
Підхід викликаного об’єкта має той недолік, що він трохи повільніший і призводить до трохи довшого коду. Однак зауважте, що колекція викликів може ділитися своїм підписом через успадкування:
class exponential(linear):
# __init__ inherited
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()
діють як функції, які спільно використовують ту саму змінну підрахунку.
Jak mogę skopiować obiekt w Pythonie?¶
Загалом, спробуйте copy.copy()
або copy.deepcopy()
для загального випадку. Не всі об'єкти можна скопіювати, але більшість можна.
Деякі об'єкти можна легше скопіювати. Словники мають метод copy()
:
newdict = olddict.copy()
Послідовності можна скопіювати шляхом нарізки:
new_l = l[:]
Як я можу знайти методи або атрибути об’єкта?¶
For an instance x
of a user-defined class, dir(x)
returns an alphabetized
list of the names containing the instance attributes and methods and attributes
defined by its class.
Як мій код може виявити назву об’єкта?¶
Загалом, не може, тому що об’єкти насправді не мають назв. По суті, присвоєння завжди прив’язує ім’я до значення; те саме стосується операторів 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>
Arguably the class has a name: even though it is bound to two names and invoked
through the name B
the created instance is still reported as an instance of
class A
. However, it is impossible to say whether the instance's name is a
or
b
, since both names are bound to the same value.
Взагалі кажучи, вашому коду не потрібно "знати імена" певних значень. Якщо ви не навмисне пишете інтроспективні програми, це зазвичай свідчить про те, що зміна підходу може бути корисною.
У comp.lang.python Фредрік Лунд якось дав чудову аналогію у відповідь на це запитання:
Так само, як ви отримуєте ім’я того кота, якого знайшли на своєму під’їзді: кіт (об’єкт) сам не може назвати вам своє ім’я, і йому це не дуже важливо, тому єдиний спосіб дізнатися, як його звуть, це запитайте всіх своїх сусідів (простір імен), чи це їхній кіт (об’єкт)...
....і не дивуйтеся, якщо ви побачите, що він відомий під багатьма іменами або взагалі без назви!
Що трапилося з пріоритетом оператора коми?¶
Кома не є оператором у Python. Розгляньте цю сесію:
>>> "a" in "b", "a"
(False, 'a')
Оскільки кома є не оператором, а роздільником між виразами, наведене вище оцінюється так, ніби ви ввели:
("a" in "b"), "a"
tidak:
"a" in ("b", "a")
Те саме стосується різних операторів присвоювання (=
, +=
тощо). Вони насправді не є операторами, а синтаксичними роздільниками в операторах присвоєння.
Чи існує еквівалент потрійного оператора C "?:"?¶
Так, є. Синтаксис такий:
[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 ...
.
Czy w Pythonie da się napisać pokręcony jednolinijkowy kod?¶
Yes. Usually this is done by nesting lambda
within
lambda
. See the following three examples, slightly adapted from Ulf Bartelt:
from functools import reduce
# Primes < 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)))))
# First 10 Fibonacci numbers
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))))
# Mandelbrot set
print((lambda Ru,Ro,Iu,Io,IM,Sx,Sy:reduce(lambda x,y:x+'\n'+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
Jangan lakukan ini di rumah, anak-anak!
Що означає коса риска (/) у списку параметрів функції?¶
A slash in the argument list of a function denotes that the parameters prior to
it are positional-only. Positional-only parameters are the ones without an
externally usable name. Upon calling a function that accepts positional-only
parameters, arguments are mapped to parameters based solely on their position.
For example, divmod()
is a function that accepts positional-only
parameters. Its documentation looks like this:
>>> help(divmod)
Help on built-in function divmod in module builtins:
divmod(x, y, /)
Return the tuple (x//y, x%y). Invariant: div*y + mod == x.
Слеш у кінці списку параметрів означає, що обидва параметри є лише позиційними. Таким чином, виклик divmod()
з ключовими аргументами призведе до помилки:
>>> divmod(x=3, y=4)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: divmod() takes no keyword arguments
Angka dan string¶
Як вказати шістнадцяткові та вісімкові цілі числа?¶
Щоб вказати вісімкову цифру, поставте перед вісімковим значенням нуль, а потім малу або велику літеру "o". Наприклад, щоб встановити змінну "a" у вісімкове значення "10" (8 у десятковій системі), введіть:
>>> a = 0o10
>>> a
8
Шістнадцяткове так само легко. Просто поставте перед шістнадцятковим числом нуль, а потім малий або великий регістр "x". Шістнадцяткові цифри можна вказувати як у нижньому, так і у верхньому регістрі. Наприклад, в інтерпретаторі Python:
>>> a = 0xa5
>>> a
165
>>> b = 0XB2
>>> b
178
Dlaczego -22 // 10 zwraca -3?¶
Головним чином це зумовлено бажанням, щоб i % j
мав той самий знак, що j
. Якщо ви цього хочете, а також хочете:
i == (i // j) * j + (i % j)
тоді цілочисельне ділення має повернути підлогу. C також вимагає збереження цієї ідентичності, а потім компіляторам, які скорочують i // j
, потрібно зробити так, щоб i % j
мав той самий знак, що і i
.
Є кілька реальних випадків використання i % j
, коли j
є від'ємним. Коли j
додатне, їх багато, і практично в усіх з них корисніше, щоб i % j
було >= 0
. Якщо годинник показує 10 зараз, що він показував 200 годин тому? -190 % 12 == 2
є корисним; -190 % 12 == -10
- це помилка, яка чекає, щоб вкусити.
Як отримати атрибут int literal замість SyntaxError?¶
Trying to lookup an int
literal attribute in the normal manner gives
a SyntaxError
because the period is seen as a decimal point:
>>> 1.__class__
File "<stdin>", line 1
1.__class__
^
SyntaxError: invalid decimal literal
Рішення полягає в тому, щоб відокремити літерал від крапки пробілом або дужками.
>>> 1 .__class__
<class 'int'>
>>> (1).__class__
<class 'int'>
Bagaimana cara mengonversi string menjadi angka?¶
For integers, use the built-in int()
type constructor, e.g. int('144')
== 144
. Similarly, float()
converts to a floating-point number,
e.g. float('144') == 144.0
.
За замовчуванням вони інтерпретують число як десяткове, так що int('0144') == 144
залишається істинним, а int('0x144')
викликає ValueError
. int(string, base)
бере основу для перетворення як другий необов’язковий аргумент, тому int( '0x144', 16) == 324
. Якщо основа вказана як 0, число інтерпретується за правилами Python: "0o" на початку означає вісімкове число, а "0x" означає шістнадцяткове число.
Не використовуйте вбудовану функцію eval()
, якщо вам потрібно лише перетворити рядки на числа. eval()
буде значно повільніше, і це становить ризик для безпеки: хтось може передати вам вираз Python, який може мати небажані побічні ефекти. Наприклад, хтось може передати __import__('os').system("rm -rf $HOME")
, що стерло б ваш домашній каталог.
eval()
також має ефект інтерпретації чисел як виразів Python, так що, наприклад, eval('09')
дає синтаксичну помилку, оскільки Python не дозволяє починати '0' у десятковому числі (окрім '0').
Bagaimana cara mengonversi angka menjadi string?¶
To convert, e.g., the number 144
to the string '144'
, use the built-in type
constructor str()
. If you want a hexadecimal or octal representation, use
the built-in functions hex()
or oct()
. For fancy formatting, see
the f-strings and Синтаксис рядка формату sections,
e.g. "{:04d}".format(144)
yields
'0144'
and "{:.3f}".format(1.0/3.0)
yields '0.333'
.
Jak zmodyfikować ciąg znaków „w miejscu”?¶
Ви не можете, тому що рядки незмінні. У більшості ситуацій вам слід просто побудувати нову струну з різних частин, з яких ви хочете її зібрати. Однак, якщо вам потрібен об’єкт із можливістю змінювати дані Unicode на місці, спробуйте використати об’єкт 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('w', s)
>>> print(a)
array('w', 'Hello, world')
>>> a[0] = 'y'
>>> print(a)
array('w', 'yello, world')
>>> a.tounicode()
'yello, world'
Як використовувати рядки для виклику функцій/методів?¶
Ada berbagai teknik.
Найкраще використовувати словник, який відображає рядки на функції. Основна перевага цього методу полягає в тому, що рядки не повинні збігатися з назвами функцій. Це також основна техніка, яка використовується для емуляції конструкції case:
def a(): pass def b(): pass dispatch = {'go': a, 'stop': b} # Note lack of parens for funcs dispatch[get_input()]() # Note trailing parens to call function
Використовуйте вбудовану функцію
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()
для визначення імені функції:def myFunc(): print("hello") fname = "myFunc" f = locals()[fname] f()
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()
цей спосіб працює добре.
Is there a scanf()
or sscanf()
equivalent?¶
Tidak seperti itu.
For simple input parsing, the easiest approach is usually to split the line into
whitespace-delimited words using the split()
method of string objects
and then convert decimal strings to numeric values using int()
or
float()
. split()
supports an optional "sep" parameter which is useful
if the line uses something other than whitespace as a separator.
For more complicated input parsing, regular expressions are more powerful
than C's sscanf
and better suited for the task.
What does UnicodeDecodeError
or UnicodeEncodeError
error mean?¶
Перегляньте Unicode HOWTO.
Can I end a raw string with an odd number of backslashes?¶
A raw string ending with an odd number of backslashes will escape the string's quote:
>>> r'C:\this\will\not\work\'
File "<stdin>", line 1
r'C:\this\will\not\work\'
^
SyntaxError: unterminated string literal (detected at line 1)
There are several workarounds for this. One is to use regular strings and double the backslashes:
>>> 'C:\\this\\will\\work\\'
'C:\\this\\will\\work\\'
Another is to concatenate a regular string containing an escaped backslash to the raw string:
>>> r'C:\this\will\work' '\\'
'C:\\this\\will\\work\\'
It is also possible to use os.path.join()
to append a backslash on Windows:
>>> os.path.join(r'C:\this\will\work', '')
'C:\\this\\will\\work\\'
Note that while a backslash will "escape" a quote for the purposes of determining where the raw string ends, no escaping occurs when interpreting the value of the raw string. That is, the backslash remains present in the value of the raw string:
>>> r'backslash\'preserved'
"backslash\\'preserved"
Also see the specification in the language reference.
Performa¶
Kode program saya berjalan lamban. Bagaimana cara saya mempercepatnya?¶
Загалом, це важко. По-перше, ось список речей, які слід запам’ятати перед подальшим зануренням:
Характеристики продуктивності відрізняються в різних реалізаціях Python. Цей FAQ присвячений CPython.
Поведінка може відрізнятися в різних операційних системах, особливо коли мова йде про введення-виведення або багатопотоковість.
Ви завжди повинні знаходити гарячі точки у своїй програмі перед спробою оптимізувати будь-який код (дивіться модуль
profile
).Написання тестових сценаріїв дозволить вам швидко виконувати ітерації під час пошуку покращень (дивіться модуль
timeit
).Настійно рекомендується добре охопити код (через модульне тестування або будь-яку іншу техніку), перш ніж потенційно вводити регресії, приховані в складних оптимізаціях.
З огляду на це, існує багато прийомів, щоб пришвидшити код Python. Ось кілька загальних принципів, які допоможуть досягти прийнятного рівня продуктивності:
Пришвидшення ваших алгоритмів (або заміна на швидші) може дати набагато більші переваги, ніж спроба розсипати трюки мікрооптимізації по всьому коду.
Використовуйте правильні структури даних. Навчальна документація для модулів Tipe Bawaan і
collections
.Коли стандартна бібліотека надає примітив для виконання чогось, швидше за все (хоча це не гарантовано) буде швидше, ніж будь-яка альтернатива, яку ви можете придумати. Це подвійно вірно для примітивів, написаних на C, таких як вбудовані модулі та деякі типи розширень. Наприклад, обов’язково використовуйте або вбудований метод
list.sort()
, або пов’язану функціюsorted()
для виконання сортування (і перегляньте Sorting Techniques для прикладів помірно просунутого використання ).Абстракції, як правило, створюють непрямість і змушують перекладача працювати більше. Якщо рівень опосередкованості переважає обсяг виконаної корисної роботи, ваша програма працюватиме повільніше. Вам слід уникати надмірної абстракції, особливо у формі крихітних функцій або методів (які також часто шкодять читабельності).
If you have reached the limit of what pure Python can allow, there are tools to take you further away. For example, Cython can compile a slightly modified version of Python code into a C extension, and can be used on many different platforms. Cython can take advantage of compilation (and optional type annotations) to make your code significantly faster than when interpreted. If you are confident in your C programming skills, you can also write a C extension module yourself.
Lihat juga
Вікі-сторінка, присвячена порадам щодо продуктивності.
Apakah cara yang paling efisien untuk menggabungkan banyak string secara bersamaan?¶
Об’єкти str
і bytes
є незмінними, тому конкатенація багатьох рядків разом неефективна, оскільки кожна конкатенація створює новий об’єкт. У загальному випадку загальна вартість виконання є квадратичною щодо загальної довжини рядка.
Щоб накопичити багато об’єктів str
, рекомендована ідіома полягає в тому, щоб помістити їх у список і викликати str.join()
в кінці:
chunks = []
for s in my_strings:
chunks.append(s)
result = ''.join(chunks)
(idiom lain yang cukup efisien adalah dengan menggunakan io.StringIO
)
Щоб накопичити багато об’єктів bytes
, рекомендована ідіома полягає в тому, щоб розширити об’єкт bytearray
за допомогою конкатенації на місці (оператор +=
):
result = bytearray()
for b in my_bytes_objects:
result += b
Urutan (Tuple/List)¶
Bagaimana cara saya mengonversi tuples dan 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[:]
.
Apa itu indeks negatif?¶
Послідовності Python індексуються позитивними та негативними числами. Для додатних чисел 0 є першим індексом, 1 є другим індексом і так далі. Для негативних індексів -1 є останнім індексом, а -2 є передостаннім (передостаннім) індексом і так далі. Подумайте про seq[-n]
як про те саме, що seq[len(seq)-n]
.
Використання від’ємних індексів може бути дуже зручним. Наприклад, S[:-1]
— це весь рядок, за винятком останнього символу, який корисний для видалення кінцевого символу нового рядка з рядка.
Як мені виконати ітерацію по послідовності у зворотному порядку?¶
Використовуйте вбудовану функцію reversed()
:
for x in reversed(sequence):
... # do something with x ...
Це не торкнеться вашої оригінальної послідовності, але створить нову копію зі зворотним порядком повторення.
Bagaimana Anda menghapus duplikasi dari list?¶
Перегляньте кулінарну книгу Python для довгого обговорення багатьох способів зробити це:
Якщо ви не проти змінити порядок списку, відсортуйте його, а потім скануйте з кінця списку, видаляючи дублікати по ходу:
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]
Якщо всі елементи списку можна використовувати як набір ключів (тобто всі вони hashable), це часто швидше
mylist = list(set(mylist))
Це перетворює список на набір, таким чином видаляючи дублікати, а потім знову на список.
Як видалити кілька елементів зі списку¶
Як і у випадку з видаленням дублікатів, явне повторення у зворотному порядку з умовою видалення є однією з можливостей. Однак простіше і швидше використовувати заміну фрагмента з неявною або явною прямою ітерацією. Ось три варіації:
mylist[:] = filter(keep_function, mylist)
mylist[:] = (x for x in mylist if keep_condition)
mylist[:] = [x for x in mylist if keep_condition]
Розуміння списку може бути найшвидшим.
Bagaimana anda membuat sebuah array di Python?¶
Gunakan sebuah list:
["this", 1, "is", "an", "array"]
За часовою складністю списки еквівалентні масивам C або Pascal; основна відмінність полягає в тому, що список Python може містити об’єкти багатьох різних типів.
The array
module also provides methods for creating arrays of fixed types
with compact representations, but they are slower to index than lists. Also
note that NumPy
and other third party packages define array-like structures with
various characteristics as well.
To get Lisp-style linked lists, you can emulate cons cells using tuples:
lisp_list = ("like", ("this", ("example", None) ) )
If mutability is desired, you could use lists instead of tuples. Here the
analogue of a Lisp car is lisp_list[0]
and the analogue of cdr is
lisp_list[1]
. Only do this if you're sure you really need to, because it's
usually a lot slower than using Python lists.
Bagaimana cara Saya membuat list multidimensi?¶
Можливо, ви намагалися створити багатовимірний масив, подібний до цього:
>>> 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)]
Or, you can use an extension that provides a matrix datatype; NumPy is the best known.
How do I apply a method or function to a sequence of objects?¶
To call a method or function and accumulate the return values is a list, a list comprehension is an elegant solution:
result = [obj.method() for obj in mylist]
result = [function(obj) for obj in mylist]
To just run the method or function without saving the return values,
a plain for
loop will suffice:
for obj in mylist:
obj.method()
for obj in mylist:
function(obj)
Чому a_tuple[i] += ['item'] викликає виключення, коли додавання працює?¶
Це пояснюється поєднанням того факту, що розширені оператори присвоєння є операторами присвоєння, а також різниці між змінними та незмінними об’єктами в Python.
Це обговорення загалом стосується випадків, коли розширені оператори присвоєння застосовуються до елементів кортежу, які вказують на змінні об’єкти, але ми будемо використовувати list
і +=
як наш приклад.
Jika kamu menulis:
>>> 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]
, що вказує на (1
), створюючи об’єкт результату 2
, але коли ми намагаємося призначити результат обчислення, 2
, елементу 0
кортежу, ми отримуємо помилку, оскільки ми не можемо змінити те, на що вказує елемент кортежу.
Під обкладинками цей доповнений оператор присвоєння робить приблизно наступне:
>>> result = a_tuple[0] + 1
>>> a_tuple[0] = result
Traceback (most recent call last):
...
TypeError: 'tuple' object does not support item assignment
Саме частина операції присвоєння створює помилку, оскільки кортеж є незмінним.
Ketika kamu menulis sesuatu seperti:
>>> 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']
To see why this happens, you need to know that (a) if an object implements an
__iadd__()
magic method, it gets called when the +=
augmented
assignment
is executed, and its return value is what gets used in the assignment statement;
and (b) for lists, __iadd__()
is equivalent to calling extend()
on the list
and returning the list. That's why we say that for lists, +=
is a
"shorthand" for list.extend()
:
>>> a_list = []
>>> a_list += [1]
>>> a_list
[1]
Ini setara dengan:
>>> result = a_list.__iadd__([1])
>>> a_list = result
Об’єкт, на який вказує a_list, було змінено, і вказівник на змінений об’єкт знову призначено a_list
. Кінцевим результатом призначення є no-op, оскільки це вказівник на той самий об’єкт, на який раніше вказував a_list
, але призначення все одно відбувається.
Таким чином, у нашому прикладі кортежу те, що відбувається, еквівалентно:
>>> result = a_tuple[0].__iadd__(['item'])
>>> a_tuple[0] = result
Traceback (most recent call last):
...
TypeError: 'tuple' object does not support item assignment
The __iadd__()
succeeds, and thus the list is extended, but even though
result
points to the same object that a_tuple[0]
already points to,
that final assignment still results in an error, because tuples are immutable.
Я хочу зробити складне сортування: чи можете ви виконати перетворення Шварца в Python?¶
Техніка, яку приписують Рендалу Шварцу зі спільноти Perl, сортує елементи списку за метрикою, яка відображає кожен елемент на його "значення сортування". У Python використовуйте аргумент key
для методу list.sort()
:
Isorted = L[:]
Isorted.sort(key=lambda s: int(s[10:15]))
Як я можу сортувати один список за значеннями з іншого списку?¶
Об’єднайте їх у ітератор кортежів, відсортуйте отриманий список, а потім виберіть потрібний елемент.
>>> 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']
Objek¶
Apa itu kelas?¶
Клас — це окремий тип об’єкта, створений шляхом виконання оператора класу. Об’єкти класу використовуються як шаблони для створення об’єктів екземплярів, які втілюють як дані (атрибути), так і код (методи), специфічні для типу даних.
Клас може базуватися на одному або кількох інших класах, які називаються базовими класами. Потім він успадковує атрибути та методи своїх базових класів. Це дозволяє послідовно вдосконалювати об’єктну модель шляхом успадкування. У вас може бути загальний клас Поштова скринька
, який надає основні методи доступу до поштової скриньки, і підкласи, такі як MboxMailbox
, MaildirMailbox
, OutlookMailbox
, які обробляють різні конкретні формати поштових скриньок.
Apa itu metode?¶
Метод — це функція для деякого об’єкта x
, який зазвичай викликається як x.name(arguments...)
. Методи визначаються як функції всередині визначення класу:
class C:
def meth(self, arg):
return arg * 2 + self.attribute
Apa itu self?¶
Self — це просто умовна назва для першого аргументу методу. Метод, визначений як meth(self, a, b, c)
, слід викликати як x.meth(a, b, c)
для деякого екземпляра x
класу, в якому відбувається визначення; викликаний метод вважатиме, що він викликається як meth(x, a, b, c)
.
Lihat juga Dlaczego 'self' musi być wyraźnie używane w definicjach metod i wywołaniach?.
Як перевірити, чи є об’єкт екземпляром заданого класу чи його підкласу?¶
Use the built-in function isinstance(obj, cls)
. You can
check if an object
is an instance of any of a number of classes by providing a tuple instead of a
single class, e.g. isinstance(obj, (class1, class2, ...))
, and can also
check whether an object is one of Python's built-in types, e.g.
isinstance(obj, str)
or isinstance(obj, (int, float, complex))
.
Зауважте, що isinstance()
також перевіряє віртуальне успадкування від abstract base class. Таким чином, тест поверне True
для зареєстрованого класу, навіть якщо він прямо чи опосередковано не успадкував його. Щоб перевірити "справжнє успадкування", проскануйте MRO класу:
from collections.abc import Mapping
class P:
pass
class C(P):
pass
Mapping.register(P)
>>> c = C()
>>> isinstance(c, C) # direct
True
>>> isinstance(c, P) # indirect
True
>>> isinstance(c, Mapping) # virtual
True
# Actual inheritance chain
>>> type(c).__mro__
(<class 'C'>, <class 'P'>, <class 'object'>)
# Test for "true inheritance"
>>> Mapping in type(c).__mro__
False
Зауважте, що більшість програм не дуже часто використовують isinstance()
у визначених користувачем класах. Якщо ви розробляєте класи самостійно, більш правильним об’єктно-орієнтованим стилем є визначення методів у класах, які інкапсулюють конкретну поведінку, замість перевірки класу об’єкта та виконання інших дій на основі того, який це клас. Наприклад, якщо у вас є функція, яка щось робить:
def search(obj):
if isinstance(obj, Mailbox):
... # code to search a mailbox
elif isinstance(obj, Document):
... # code to search a document
elif ...
Кращий підхід полягає в тому, щоб визначити метод search()
для всіх класів і просто викликати його:
class Mailbox:
def search(self):
... # code to search a mailbox
class Document:
def search(self):
... # code to search a document
obj.search()
Apa itu delegasi?¶
Делегування — це об’єктно-орієнтована техніка (яка також називається шаблоном проектування). Припустімо, у вас є об’єкт 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)
Here the UpperOut
class redefines the write()
method to convert the
argument string to uppercase before calling the underlying
self._outfile.write()
method. All other methods are delegated to the
underlying self._outfile
object. The delegation is accomplished via the
__getattr__()
method; consult the language reference
for more information about controlling attribute access.
Note that for more general cases delegation can get trickier. When attributes
must be set as well as retrieved, the class must define a __setattr__()
method too, and it must do so carefully. The basic implementation of
__setattr__()
is roughly equivalent to the following:
class X:
...
def __setattr__(self, name, value):
self.__dict__[name] = value
...
Many __setattr__()
implementations call object.__setattr__()
to set
an attribute on self without causing infinite recursion:
class X:
def __setattr__(self, name, value):
# Custom logic here...
object.__setattr__(self, name, value)
Alternatively, it is possible to set attributes by inserting
entries into self.__dict__
directly.
How do I call a method defined in a base class from a derived class that extends it?¶
Використовуйте вбудовану функцію super()
:
class Derived(Base):
def meth(self):
super().meth() # calls Base.meth
In the example, super()
will automatically determine the instance from
which it was called (the self
value), look up the method resolution
order (MRO) with type(self).__mro__
, and return the next in line after
Derived
in the MRO: Base
.
Як я можу організувати свій код, щоб полегшити зміну базового класу?¶
Ви можете призначити базовий клас псевдоніму та отримати його від псевдоніма. Тоді все, що вам потрібно змінити, це значення, присвоєне псевдоніму. До речі, цей прийом також зручний, якщо ви хочете динамічно (наприклад, залежно від наявності ресурсів) вирішувати, який базовий клас використовувати. Приклад:
class Base:
...
BaseAlias = Base
class Derived(BaseAlias):
...
Як створити статичні дані класу та статичні методи класу?¶
У Python підтримуються як статичні дані, так і статичні методи (у сенсі C++ або Java).
Для статичних даних просто визначте атрибут класу. Щоб призначити нове значення атрибуту, ви повинні явно використовувати ім’я класу в призначенні:
class C:
count = 0 # number of times C.__init__ called
def __init__(self):
C.count = C.count + 1
def getcount(self):
return C.count # or return 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):
# No 'self' parameter!
...
Однак набагато більш простий спосіб отримати ефект статичного методу — це за допомогою простої функції на рівні модуля:
def getcount():
return C.count
Якщо ваш код структурований таким чином, щоб визначити один клас (або тісно пов’язану ієрархію класів) на модуль, це забезпечує бажану інкапсуляцію.
Як я можу перевантажити конструктори (або методи) у Python?¶
Ця відповідь фактично стосується всіх методів, але зазвичай це питання виникає першим у контексті конструкторів.
Di C++ kamu akan menulis
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):
...
Той самий підхід працює для всіх визначень методів.
Я намагаюся використовувати __спам і отримую помилку про _SomeClassName__спам.¶
Назви змінних із подвійним підкресленням на початку "спотворені", щоб забезпечити простий, але ефективний спосіб визначення приватних змінних класу. Будь-який ідентифікатор у формі __spam
(принаймні два символи підкреслення на початку, не більше одного символу підкреслення в кінці) текстово замінюється на _classname__spam
, де classname
є поточною назвою класу з будь-якими початковими символами підкреслення.
The identifier can be used unchanged within the class, but to access it outside the class, the mangled name must be used:
class A:
def __one(self):
return 1
def two(self):
return 2 * self.__one()
class B(A):
def three(self):
return 3 * self._A__one()
four = 4 * A()._A__one()
In particular, this does not guarantee privacy since an outside user can still deliberately access the private attribute; many Python programmers never bother to use private variable names at all.
Lihat juga
The private name mangling specifications for details and special cases.
Мій клас визначає __del__, але він не викликається, коли я видаляю об’єкт.¶
Для цього є кілька можливих причин.
The del
statement does not necessarily call __del__()
-- it simply
decrements the object's reference count, and if this reaches zero
__del__()
is called.
If your data structures contain circular links (e.g. a tree where each child has
a parent reference and each parent has a list of children) the reference counts
will never go back to zero. Once in a while Python runs an algorithm to detect
such cycles, but the garbage collector might run some time after the last
reference to your data structure vanishes, so your __del__()
method may be
called at an inconvenient and random time. This is inconvenient if you're trying
to reproduce a problem. Worse, the order in which object's __del__()
methods are executed is arbitrary. You can run gc.collect()
to force a
collection, but there are pathological cases where objects will never be
collected.
Despite the cycle collector, it's still a good idea to define an explicit
close()
method on objects to be called whenever you're done with them. The
close()
method can then remove attributes that refer to subobjects. Don't
call __del__()
directly -- __del__()
should call close()
and
close()
should make sure that it can be called more than once for the same
object.
Іншим способом уникнути циклічних посилань є використання модуля weakref
, який дозволяє вам вказувати на об’єкти, не збільшуючи їх кількість посилань. Деревоподібні структури даних, наприклад, повинні використовувати слабкі посилання для своїх батьківських і братських посилань (якщо вони їм потрібні!).
Finally, if your __del__()
method raises an exception, a warning message
is printed to sys.stderr
.
Як отримати список усіх екземплярів певного класу?¶
Python не відстежує всі екземпляри класу (або вбудованого типу). Ви можете запрограмувати конструктор класу для відстеження всіх екземплярів, зберігаючи список слабких посилань на кожен екземпляр.
Чому результат id()
здається не унікальним?¶
Вбудований id()
повертає ціле число, яке гарантовано буде унікальним протягом усього життя об’єкта. Оскільки в CPython це адреса пам’яті об’єкта, часто трапляється так, що після видалення об’єкта з пам’яті наступний щойно створений об’єкт розміщується в тій же позиції в пам’яті. Це проілюстровано таким прикладом:
>>> id(1000)
13901272
>>> id(2000)
13901272
Два ідентифікатори належать до різних цілочисельних об’єктів, створених раніше та видалених одразу після виконання виклику id()
. Щоб переконатися, що об’єкти, чий ідентифікатор ви хочете перевірити, все ще живі, створіть інше посилання на об’єкт:
>>> a = 1000; b = 2000
>>> id(a)
13901272
>>> id(b)
13891296
Коли я можу покладатися на перевірку ідентичності з оператором is?¶
Оператор is
перевіряє ідентичність об'єкта. Перевірка "a is b" еквівалентна "id(a) == id(b)".
Найважливіша властивість перевірки ідентичності полягає в тому, що об’єкт завжди ідентичний самому собі, a is a
завжди повертає True
. Тести ідентифікації зазвичай швидші, ніж тести рівності. І на відміну від тестів на рівність, тести ідентичності гарантовано повертають логічне значення True
або False
.
Однак тести на ідентичність можна тільки замінити на тести на рівність, якщо ідентичність об’єкта забезпечена. Загалом існує три обставини, за яких ідентичність гарантується:
Assignments create new names but do not change object identity. After the assignment
new = old
, it is guaranteed thatnew is old
.Putting an object in a container that stores object references does not change object identity. After the list assignment
s[0] = x
, it is guaranteed thats[0] is x
.If an object is a singleton, it means that only one instance of that object can exist. After the assignments
a = None
andb = None
, it is guaranteed thata is b
becauseNone
is a singleton.
У більшості інших обставин тести на ідентичність є недоцільними, а тести на рівність є кращими. Зокрема, тести ідентичності не слід використовувати для перевірки таких констант, як int
і str
, які не гарантовано є одиночними:
>>> a = 1000
>>> b = 500
>>> c = b + 500
>>> a is c
False
>>> a = 'Python'
>>> b = 'Py'
>>> c = b + 'thon'
>>> a is c
False
Подібним чином нові екземпляри змінних контейнерів ніколи не бувають ідентичними:
>>> a = []
>>> b = []
>>> a is b
False
У коді стандартної бібліотеки ви побачите кілька загальних шаблонів для правильного використання тестів ідентичності:
As recommended by PEP 8, an identity test is the preferred way to check for
None
. This reads like plain English in code and avoids confusion with other objects that may have boolean values that evaluate to false.Detecting optional arguments can be tricky when
None
is a valid input value. In those situations, you can create a singleton sentinel object guaranteed to be distinct from other objects. For example, here is how to implement a method that behaves likedict.pop()
:_sentinel = object() def pop(self, key, default=_sentinel): if key in self: value = self[key] del self[key] return value if default is _sentinel: raise KeyError(key) return default
Container implementations sometimes need to augment equality tests with identity tests. This prevents the code from being confused by objects such as
float('NaN')
that are not equal to themselves.
For example, here is the implementation of
collections.abc.Sequence.__contains__()
:
def __contains__(self, value):
for v in self:
if v is value or v == value:
return True
return False
Як підклас може контролювати, які дані зберігаються в незмінному екземплярі?¶
When subclassing an immutable type, override the __new__()
method
instead of the __init__()
method. The latter only runs after an
instance is created, which is too late to alter data in an immutable
instance.
Усі ці незмінні класи мають інший підпис, ніж їх батьківський клас:
from datetime import date
class FirstOfMonthDate(date):
"Always choose the first day of the month"
def __new__(cls, year, month, day):
return super().__new__(cls, year, month, 1)
class NamedInt(int):
"Allow text names for some numbers"
xlat = {'zero': 0, 'one': 1, 'ten': 10}
def __new__(cls, value):
value = cls.xlat.get(value, value)
return super().__new__(cls, value)
class TitleStr(str):
"Convert str to name suitable for a URL path"
def __new__(cls, s):
s = s.lower().replace(' ', '-')
s = ''.join([c for c in s if c.isalnum() or c == '-'])
return super().__new__(cls, s)
Класи можна використовувати так:
>>> FirstOfMonthDate(2012, 2, 14)
FirstOfMonthDate(2012, 2, 1)
>>> NamedInt('ten')
10
>>> NamedInt(20)
20
>>> TitleStr('Blog: Why Python Rocks')
'blog-why-python-rocks'
How do I cache method calls?¶
The two principal tools for caching methods are
functools.cached_property()
and functools.lru_cache()
. The
former stores results at the instance level and the latter at the class
level.
The cached_property approach only works with methods that do not take any arguments. It does not create a reference to the instance. The cached method result will be kept only as long as the instance is alive.
The advantage is that when an instance is no longer used, the cached method result will be released right away. The disadvantage is that if instances accumulate, so too will the accumulated method results. They can grow without bound.
The lru_cache approach works with methods that have hashable arguments. It creates a reference to the instance unless special efforts are made to pass in weak references.
The advantage of the least recently used algorithm is that the cache is bounded by the specified maxsize. The disadvantage is that instances are kept alive until they age out of the cache or until the cache is cleared.
This example shows the various techniques:
class Weather:
"Lookup weather information on a government website"
def __init__(self, station_id):
self._station_id = station_id
# The _station_id is private and immutable
def current_temperature(self):
"Latest hourly observation"
# Do not cache this because old results
# can be out of date.
@cached_property
def location(self):
"Return the longitude/latitude coordinates of the station"
# Result only depends on the station_id
@lru_cache(maxsize=20)
def historic_rainfall(self, date, units='mm'):
"Rainfall on a given date"
# Depends on the station_id, date, and units.
The above example assumes that the station_id never changes. If the relevant instance attributes are mutable, the cached_property approach can't be made to work because it cannot detect changes to the attributes.
To make the lru_cache approach work when the station_id is mutable,
the class needs to define the __eq__()
and __hash__()
methods so that the cache can detect relevant attribute updates:
class Weather:
"Example with a mutable station identifier"
def __init__(self, station_id):
self.station_id = station_id
def change_station(self, station_id):
self.station_id = station_id
def __eq__(self, other):
return self.station_id == other.station_id
def __hash__(self):
return hash(self.station_id)
@lru_cache(maxsize=20)
def historic_rainfall(self, date, units='cm'):
'Rainfall on a given date'
# Depends on the station_id, date, and units.
Modul-Modul¶
Bagaimana saya membuat berkas .pyc?¶
Коли модуль імпортується вперше (або якщо вихідний файл змінився після створення поточного скомпільованого файлу), файл .pyc
, що містить скомпільований код, має бути створений у підкаталозі __pycache__
каталог, що містить файл .py
. Файл .pyc
матиме назву файлу, яка починається з тієї самої назви, що й файл .py
, і закінчується .pyc
, із середнім компонентом, який залежить від конкретного python
двійковий файл, який його створив. (Докладніше див. PEP 3147.)
Однією з причин того, що файл .pyc
може не бути створений, є проблема з дозволами для каталогу, що містить вихідний файл, тобто неможливо створити підкаталог __pycache__
. Це може статися, наприклад, якщо ви розробляєте як один користувач, але запускаєте як інший, наприклад, якщо ви тестуєте за допомогою веб-сервера.
Якщо не встановлено змінну середовища PYTHONDONTWRITEBYTECODE
, створення файлу .pyc відбувається автоматично, якщо ви імпортуєте модуль і Python має можливість (дозволи, вільне місце тощо) створити __pycache__
підкаталог і записати скомпільований модуль у цей підкаталог.
Запуск Python на сценарії верхнього рівня не вважається імпортом, і .pyc
не буде створено. Наприклад, якщо у вас є модуль верхнього рівня foo.py
, який імпортує інший модуль xyz.py
, коли ви запускаєте foo
(ввівши python foo.py
як команду оболонки), для xyz
буде створено .pyc
, оскільки xyz
імпортовано, але файл .pyc
не буде створено для foo
, оскільки foo.py
не імпортується.
Якщо вам потрібно створити файл .pyc
для foo
, тобто створити файл .pyc
для модуля, який не імпортується, ви можете, використовувати модулі py_compile
і compileall
.
Модуль py_compile
може вручну скомпілювати будь-який модуль. Одним із способів є використання функції compile()
у цьому модулі в інтерактивному режимі:
>>> import py_compile
>>> py_compile.compile('foo.py')
Це запише .pyc
до підкаталогу __pycache__
в тому самому місці, що foo.py
(або ви можете змінити це за допомогою додаткового параметра cfile
).
Ви також можете автоматично скомпілювати всі файли в каталозі або каталогах за допомогою модуля compileall
. Ви можете зробити це з підказки оболонки, запустивши compileall.py
і вказавши шлях до каталогу, що містить файли Python для компіляції:
python -m compileall .
Як знайти поточну назву модуля?¶
Модуль може дізнатися власну назву модуля, дивлячись на попередньо визначену глобальну змінну __name__
. Якщо має значення '__main__
, програма виконується як сценарій. Багато модулів, які зазвичай використовуються шляхом їх імпорту, також забезпечують інтерфейс командного рядка або самоперевірку та виконують цей код лише після перевірки __name__
:
def main():
print('Running test...')
...
if __name__ == '__main__':
main()
Як я можу мати модулі, які взаємно імпортують один одного?¶
Припустимо, у вас є такі модулі:
foo.py
:
from bar import bar_var
foo_var = 1
bar.py
:
from foo import foo_var
bar_var = 2
Проблема полягає в тому, що перекладач виконуватиме наступні кроки:
main imports
foo
Empty globals for
foo
are createdfoo
is compiled and starts executingfoo
importsbar
Empty globals for
bar
are createdbar
is compiled and starts executingbar
importsfoo
(which is a no-op since there already is a module namedfoo
)The import mechanism tries to read
foo_var
fromfoo
globals, to setbar.foo_var = foo.foo_var
Останній крок не вдається, оскільки Python ще не завершив інтерпретацію foo
, а глобальний словник символів для foo
все ще порожній.
Те ж саме відбувається, коли ви використовуєте import foo
, а потім намагаєтесь отримати доступ foo.foo_var
у глобальному коді.
Є (принаймні) три можливі способи вирішення цієї проблеми.
Гвідо ван Россум рекомендує уникати будь-якого використання from <module> import ...
і розміщувати весь код у функціях. Для ініціалізації глобальних змінних і змінних класу слід використовувати лише константи або вбудовані функції. Це означає, що все з імпортованого модуля посилається як <module> . <name>
.
Джим Роскінд пропонує виконувати кроки в такому порядку в кожному модулі:
експорт (глобальні елементи, функції та класи, яким не потрібні імпортовані базові класи)
pernyataan
import
активний код (включаючи глобальні значення, ініціалізовані з імпортованих значень).
Ван Россуму не дуже подобається такий підхід, оскільки імпорт з’являється в незнайомому місці, але він працює.
Матіас Урліхс рекомендує реструктуризувати ваш код, щоб рекурсивний імпорт не був необхідним.
Ці рішення не є взаємовиключними.
__import__('x.y.z') повертає <module 'x'> ; як я можу отримати z?¶
Замість цього можна скористатися зручною функцією import_module()
з importlib
:
z = importlib.import_module('x.y.z')
Коли я редагую імпортований модуль і повторно імпортую його, зміни не відображаються. чому це відбувається¶
З міркувань ефективності та узгодженості Python читає файл модуля лише під час першого імпорту модуля. Якби цього не було, у програмі, що складається з багатьох модулів, кожен з яких імпортує той самий базовий модуль, базовий модуль аналізувався б і повторно аналізувався багато разів. Щоб примусово перечитати змінений модуль, виконайте наступне:
import importlib
import modname
importlib.reload(modname)
Попередження: ця техніка не є на 100% надійною. Зокрема, модулі, що містять оператори типу
from modname import some_objects
продовжить працювати зі старою версією імпортованих об'єктів. Якщо модуль містить визначення класу, існуючі екземпляри класу не будуть оновлені для використання нового визначення класу. Це може призвести до наступної парадоксальної поведінки:
>>> import importlib
>>> import cls
>>> c = cls.C() # Create an instance of C
>>> importlib.reload(cls)
<module 'cls' from 'cls.py'>
>>> isinstance(c, cls.C) # isinstance is false?!?
False
Sifat masalah dibuat jelas jika Anda mencetak "identitas" objek kelas:
>>> hex(id(c.__class__))
'0x7352a0'
>>> hex(id(cls.C))
'0x4198d0'