На GitHub есть проект под названием «Что за х*й Python!», этот интересный проект направлен на то, чтобы собрать эти непонятные и контринтуитивные примеры и малоизвестные функции в Python, и попытаться обсудить причины этих явлений. ! Оригинальный адрес:GitHub.com/Sat Проверьте SA для меня…
Недавно он был переведен на китайский автором по имени «Му Чен». Адрес китайской версии:GitHub.com/leasure стоит…
Первоначально каждое название было на английском языке в оригинальной версии. Некоторые названия были странными и неинтуитивными. Я изменил их на китайскую форму, которая может описать тему. Некоторые из них являются моими собственными мыслями. Пожалуйста, поправьте меня. ДругиеPythonЯ удалил пасхальные яйца в файле .
Я лично испробовал все коды и добавил некоторые из своих собственных представлений и примеров, поэтому текст будет немного отличаться от исходного.
Опубликована следующая статья:Python: малоизвестные функции (часть 2)
1. Резиденция строки
①
>>> a = '!'
>>> b = '!'
>>> a is b
True
②
>>> a = 'some_string'
>>> id(a)
140420665652016
>>> id('some' + '_' + 'string') # 注意两个的id值是相同的.
140420665652016
③
>>> a = 'wtf'
>>> b = 'wtf'
>>> a is b
True
>>> a = 'wtf!'
>>> b = 'wtf!'
>>> a is b
False
>>> a, b = 'wtf!', 'wtf!'
>>> a is b
True
④
>>> 'a' * 20 is 'aaaaaaaaaaaaaaaaaaaa'
True
>>> 'a' * 21 is 'aaaaaaaaaaaaaaaaaaaaa'
False
проиллюстрировать:Эти действия обусловленыCPythonПри компиляции и оптимизации в некоторых случаях вы будете пытаться создать новый объект вместо того, чтобы каждый раз создавать новый объект. Такое поведение называется резиденцией строкиstring interning. После того, как происходит резидентность, многие переменные могут указывать на один и тот же строковый объект в памяти для экономии памяти.
Есть несколько методов, которые можно использовать для определения того, будет ли строка резидентной:
- Все строки длины 0 и длины 1 являются резидентными (строки в ① являются резидентными)
- Строки реализуются во время компиляции (
'wtf'
будет резидентом, но''.join(['w', 't', 'f']
Не будет резидентом) - будет находиться, когда строка содержит только буквы, цифры или символы подчеркивания, поэтому
'wtf!'
из-за включения'!'
не остаться - когда на той же линии будет
a
а такжеb
Значение установлено на'wtf!'
Переводчик Python создает новый объект, и оба переменных указывают на этот объект одновременно. Если вы выполните назначение на другую строку, это не будет «знать», что уже есть один'wtf!'
предмет (потому что'wtf!'
не является неявным резидентом, как указано выше). - постоянное складывание (constant folding)ДаPythonОптимизация глазка в (peephole optimization)Технология. Это означает, что во время компиляции выражение
'a' * 20
будет заменен на'aaaaaaaaaaaaaaaaaaaa'
для уменьшения тактов времени выполнения. Константное свертывание происходит только для строк длиной менее 20. (Почему? Представьте, что благодаря выражению'a' * 10 ** 10
и размер результирующего файла .pyc).
Если вы попробуете этот пример в файле .py, вы не увидите того же поведения, потому что файл компилируется за один раз.
2. Ключи словаря
>>> some_dict = {}
>>> some_dict[5.5] = "Ruby"
>>> some_dict[5.0] = "JavaScript"
>>> some_dict[5] = "Python"
>>> some_dict[5.5]
"Ruby"
>>> some_dict[5.0]
"Python"
>>> some_dict[5]
"Python"
проиллюстрировать:
PythonСловарные проверки на равенство ключей определяются путем сравнения хэшей на равенство. Если два объекта равны при сравнении, то их хеш-значения должны быть равны, иначе хеш-таблица не будет работать должным образом. Например, если1 == 1.0
правда, тоhash(1) == hash(1.0)
Тоже должно быть верно, но внутренняя структура двух чисел (целое и число с плавающей запятой) совершенно разная.
3. return в предложении finally
def some_func():
try:
return 'from_try'
finally:
return 'from_finally'
Output:
>>> some_func()
'from_finally'
проиллюстрировать:Возвращаемое значение функции определяется последним выполненнымreturn
Решение приговора. из-заfinally
пункт должен быть выполнен, поэтомуfinally
в пунктеreturn
всегда будет последним выполненным оператором.
4. Тот же объект
class WTF:
pass
Output:
>>> WTF() == WTF() # 两个不同的对象应该不相等
False
>>> WTF() is WTF() # 也不相同
False
>>> hash(WTF()) == hash(WTF()) # 哈希值也应该不同
True
>>> id(WTF()) == id(WTF())
True
проиллюстрировать:при звонкеid()
функция,PythonсоздалWTF
объект класса и передаетсяid()
функция, тоid()
Функция получает значение своего идентификатора (то есть адрес памяти), затем отбрасывает объект, и объект уничтожается.
Когда мы делаем это дважды подряд, Python присваивает один и тот же адрес памяти второму объекту, потому что вCPythonсерединаid()
Функция использует адрес памяти объекта в качестве значения идентификатора объекта, поэтому значение идентификатора обоих объектов одинаково.
Подводя итог, можно сказать, что значение идентификатора объекта уникально только в течение жизненного цикла объекта.После уничтожения объекта или до его создания другие объекты могут иметь такое же значение идентификатора.
class WTF(object):
def __init__(self): print("I")
def __del__(self): print("D")
Output:
>>> WTF() is WTF()
I
I
D
D
False
>>> id(WTF()) == id(WTF())
I
D
I
D
True
Как видите, порядок, в котором уничтожаются объекты, имеет значение.
5. для назначения петли целевое назначение
>>> some_string = "wtf"
>>> some_dict = {}
>>> for i, some_dict[i] in enumerate(some_string): pass
>>> some_dict
{0: 'w', 1: 't', 2: 'f'}
проиллюстрировать:Это легко понять, если внимательно присмотреться.for
Каждая итерация цикла присваивает значение цели присваивания,some_dict[i] = value
Это эквивалентно добавлению пар ключ-значение в словарь.
Интересен приведенный ниже пример. Вы когда-нибудь думали, что цикл будет выполняться только один раз?
for i in range(4):
print(i)
i = 10
6. Разница во времени выполнения
①
>>> array = [1, 8, 15]
>>> g = (x for x in array if array.count(x) > 0)
>>> array = [2, 8, 22]
>>> list(g)
[8]
②
>>> array_1 = [1, 2, 3, 4]
>>> g1 = (x for x in array_1)
>>> array_1 = [1, 2, 3, 4, 5]
>>> array_2 = [1, 2, 3, 4]
>>> g2 = (x for x in array_2)
>>> array_2[:] = [1, 2, 3, 4, 5]
>>> list(g1)
[1, 2, 3, 4]
>>> list(g2)
[1, 2, 3, 4, 5]
проиллюстрировать:в выражении генератораin
Предложения выполняются во время объявления, а условные предложения выполняются во время выполнения.
① в, перед запускомarray
был переведен на[2, 8, 22]
, поэтому для предыдущих 1, 8, 15 толькоcount(8)
Результат больше 0, поэтому генератор сгенерирует только 8.
② Средний,g1
а такжеg2
Разница в выходе обусловлена переменнойarray_1
а такжеarray_2
Вызвано тем, как он был переназначен.
- В первом случае
array_1
привязан к новому объекту[1, 2, 3, 4, 5]
,потому чтоin
предложение выполняется во время объявления, поэтому оно по-прежнему ссылается на старый объект[1, 2, 3, 4]
(и не уничтожен). - Во втором случае правильно
array_2
Назначение среза будет таким же, как у старого объекта.[1, 2, 3, 4]
Обновление на месте до[1, 2, 3, 4, 5]
. Поэтому g2 иarray_2
по-прежнему относятся к одному и тому же объекту[1, 2, 3, 4, 5]
.
7. Предварительно выделенное целое число
>>> a = 256
>>> b = 256
>>> a is b
True
>>> a = 257
>>> b = 257
>>> a is b
False
>>> a = 257; b = 257
>>> a is b
True
Разница между есть и ==
-
is
оператор проверяет, относятся ли два операнда к одному и тому же объекту -
==
оператор сравнивает два операнда на равенство
следовательноis
означает ту же ссылку,==
Репрезентативное значение равно. Следующий пример может быть хорошо описан:
>>> [] == []
True
>>> [] is [] # 这两个空列表位于不同的内存地址
False
256 — это существующий объект, а 257 — нет.
при запускеPython, значения от -5 до 256 уже присвоены. Эти номера подходят для подготовки заранее, потому что они часто используются.
Текущая реализация хранит массив целочисленных объектов для всех целых чисел от -5 до 256, и когда вы создаете целое число в этом диапазоне, вы просто возвращаете ссылку на существующий объект. Таким образом, можно изменить значение 1.
Однако, когдаa
а такжеb
При инициализации одним и тем же значением в той же строке он указывает на тот же объект.
>>> id(256)
10922528
>>> a = 256
>>> b = 256
>>> id(a)
10922528
>>> id(b)
10922528
>>> id(257)
140084850247312
>>> x = 257
>>> y = 257
>>> id(x)
140084850247440
>>> id(y)
140084850247344
>>> a, b = 257, 257
>>> id(a)
140640774013296
>>> id(b)
140640774013296
Это оптимизация компилятора, сделанная специально для интерактивной среды, когда вы вводите две строки в живой интерпретатор, они компилируются отдельно и, следовательно, оптимизируются отдельно, если вы попробуете этот пример в файле .py, вы не увидите такого же поведения. потому что файл компилируется за один раз.
8. Непреднамеренное присвоение ссылочных типов
>>> row = [''] * 3
>>> board = [row] * 3
>>> board
[['', '', ''], ['', '', ''], ['', '', '']]
>>> board[0]
['', '', '']
>>> board[0][0]
''
>>> board[0][0] = "X"
>>> board
[['X', '', ''], ['X', '', ''], ['X', '', '']]
проиллюстрировать:Выведем id и посмотрим:
>>> id(row[0])
7536232
>>> id(row[1])
5143216
>>> id(row[2])
5143216
>>> id(board[0])
7416840
>>> id(board[1])
7416840
>>> id(board[2])
7416840
row
представляет собой список со всеми тремя элементами, указывающими на адрес 5143216, когдаboard[0][0]
После назначения,row
的第一个元素指向 7536232。 а такжеboard
Все три элемента вrow
,row
Адрес не изменился.
Мы можем сделать это, не используя переменныеrow
генерироватьboard
чтобы избежать этого.
>>> board = [[''] * 3 for _ in range(3)]
>>> board[0][0] = "X"
>>> board
[['X', '', ''], ['', '', ''], ['', '', '']]
Здесь используется вывод, и каждая итерация будет генерировать новый_
,такboard
Три элемента указывают на разные переменные.
9. Функция закрытия
funcs = []
results = []
for x in range(7):
def some_func():
return x
funcs.append(some_func)
results.append(some_func())
funcs_results = [func() for func in funcs]
Output:
>>> results
[0, 1, 2, 3, 4, 5, 6]
>>> funcs_results
[6, 6, 6, 6, 6, 6, 6]
проиллюстрировать:При определении функции внутри цикла, если функция использует переменную цикла в своем теле, функция замыкания будет привязана к переменной цикла, а не к ее значению. Поэтому все функции рассчитываются с использованием последнего значения, присвоенного переменной.
Ожидаемый результат можно получить, передав в функцию переменную цикла как именованную переменную. Почему это работает? Потому что это снова определит локальную переменную внутри функции.
funcs = []
for x in range(7):
def some_func(x=x):
return x
funcs.append(some_func)
Output:
>>> funcs_results = [func() for func in funcs]
>>> funcs_results
[0, 1, 2, 3, 4, 5, 6]
10. Обратная косая черта в конце строки
>>> print("\\ C:\\")
\ C:\
>>> print(r"\ C:")
\ C:
>>> print(r"\ C:\")
File "<stdin>", line 1
print(r"\ C:\")
^
SyntaxError: EOL while scanning string literal
проиллюстрировать:вr
В необработанных строках в начале обратная косая черта не имеет особого значения. Все, что делает интерпретатор, — это просто меняет поведение обратной косой черты, чтобы она передавалась напрямую.обратная косая черта и следующий символ. Вот почему обратная косая черта не работает в конце необработанных строк.
11. Приоритет операторов == и not
>>> not x == y
True
>>> x == not y
File "<input>", line 1
x == not y
^
SyntaxError: invalid syntax
проиллюстрировать:Одним словом,==
оператор имеет более высокий приоритет, чемnot
оператор.
12. Тройные кавычки
>>> print('wtfpython''')
wtfpython
>>> print("wtfpython""")
wtfpython
>>> # 下面的语句会抛出 `SyntaxError` 异常
>>> # print('''wtfpython')
>>> # print("""wtfpython")
проиллюстрировать:
'''
а также"""
существуетPythonтакже является разделителем строк,PythonКогда интерпретатор сначала встретит три кавычки, он попытается найти три завершающих кавычки в качестве разделителей, и если они не существуют, это приведет кSyntaxError
аномальный.
В то время как Python обеспечивает неявную цепочку строк:
>>> print("wtf" "python")
wtfpython
>>> print("wtf""") # 相当于 "wtf" ""
wtf
13. Исчезающая полночь
from datetime import datetime
midnight = datetime(2018, 1, 1, 0, 0)
midnight_time = midnight.time()
noon = datetime(2018, 1, 1, 12, 0)
noon_time = noon.time()
if midnight_time:
print("Time at midnight is", midnight_time)
if noon_time:
print("Time at noon is", noon_time)
Output:
Time at noon is 12:00:00
midnight_time
не выводится.
проиллюстрировать:существуетPython 3.5раньше, еслиdatetime.time
Объект сохраняется в полночь UTC, тогда его логическое значение будет считаться False.
я сделал это специальноpython 3.4Подтвердите, это правда.
14. логическое значение
mixed_list = [False, 1.0, "some_string", 3, True, [], False]
integers_found_so_far = 0
booleans_found_so_far = 0
for item in mixed_list:
if isinstance(item, int):
integers_found_so_far += 1
elif isinstance(item, bool):
booleans_found_so_far += 1
Output:
>>> booleans_found_so_far
0
>>> integers_found_so_far
4
проиллюстрировать:логическое значениеint
подкласс
>>> isinstance(True, int)
True
>>> isinstance(False, int)
True
В фактическом введенииbool
До типов 0 и 1 были официальным представлением значений истинности. Для обратной совместимости новыйbool
Типы должны работать как 0 и 1.
15. Атрибуты класса и атрибуты экземпляра
①
class A:
x = 1
class B(A):
pass
class C(A):
pass
Output:
>>> A.x, B.x, C.x
(1, 1, 1)
>>> B.x = 2
>>> A.x, B.x, C.x
(1, 2, 1)
>>> A.x = 3
>>> A.x, B.x, C.x
(3, 2, 3)
>>> a = A()
>>> a.x, A.x
(3, 3)
>>> a.x += 1
>>> a.x, A.x
(4, 3)
②
class SomeClass:
some_var = 15
some_list = [5]
another_list = [5]
def __init__(self, x):
self.some_var = x + 1
self.some_list = self.some_list + [x]
self.another_list += [x]
Output:
>>> some_obj = SomeClass(420)
>>> some_obj.some_list
[5, 420]
>>> some_obj.another_list
[5, 420]
>>> another_obj = SomeClass(111)
>>> another_obj.some_list
[5, 111]
>>> another_obj.another_list
[5, 420, 111]
>>> another_obj.another_list is SomeClass.another_list
True
>>> another_obj.another_list is some_obj.another_list
True
проиллюстрировать:
- Переменные класса и переменные экземпляра обрабатываются внутри через словарь объектов класса (
__dict__
атрибут), если он не найден в словаре текущего класса, он будет искать в своем родительском классе. -
+=
Операторы изменяют изменяемые объекты на месте, а не создают новые. Следовательно, изменение свойства одного экземпляра влияет на другие свойства экземпляра и класса.
16. Ошибка доходности
some_iterable = ('a', 'b')
def some_func(val):
return "something"
Output:
>>> [x for x in some_iterable]
['a', 'b']
>>> [(yield x) for x in some_iterable]
<generator object <listcomp> at 0x7f70b0a4ad58>
>>> list([(yield x) for x in some_iterable])
['a', 'b']
>>> list((yield x) for x in some_iterable)
['a', None, 'b', None]
>>> list(some_func((yield x)) for x in some_iterable)
['a', 'something', 'b', 'something']
проиллюстрировать:ЭтоCPythonОбработка вложений и выражений генератораyield
ошибка вPython 3.8ремонт вPython 3.7Есть предупреждения об устаревании. видетьPythonотчеты об ошибках иPython 3.7а такжеPython 3.8новая запись.
Источник и объяснение можно найти здесь:stackoverflow.com/questions/3…Связанные отчеты об ошибках:bugs.python.org/issue10544
17. Относительная неизменность кортежей
>>> some_tuple = ("A", "tuple", "with", "values")
>>> another_tuple = ([1, 2], [3, 4], [5, 6])
>>> some_tuple[2] = "change this"
TypeError: 'tuple' object does not support item assignment
>>> another_tuple[2].append(1000) # 这里不出现错误
>>> another_tuple
([1, 2], [3, 4], [5, 6, 1000])
>>> another_tuple[2] += [99, 999]
TypeError: 'tuple' object does not support item assignment
>>> another_tuple
([1, 2], [3, 4], [5, 6, 1000, 99, 999])
проиллюстрировать:Идентификатор неизменяемого элемента в кортеже (то есть адрес элемента). Если элемент является ссылочным типом, значение кортежа будет меняться при изменении ссылочного изменяемого объекта. такanother_tuple[2].append(1000)
разрешено.+=
Оператор изменяет список на месте. Назначение элемента не работает, но при возникновении исключения элемент был изменен на месте.+=
не атомарные операции, аextend
а также=
два действия, здесь=
Хотя операция выдает исключение,extend
Операция успешно изменена.
18. Исчезающие внешние переменные
e = 7
try:
raise Exception()
except Exception as e:
pass
Output: python2
>>> print(e)
# prints nothing
Output: python3
>>> print(e)
NameError: name 'e' is not defined
проиллюстрировать:когда используешьas
При назначении исключения цели оно будет вexcept
Исключение очищается в конце предложения.
Это как:
except E as N:
foo
будет переведено в:
except E as N:
try:
foo
finally:
del N
Это означает, что исключению должно быть присвоено другое имя, чтобыexcept
обратитесь к нему после пункта. Исключение очищается, так как добавляется информация о трассировке (trackback), они связаны с кадрами стека (stack frame) формирует ссылочный цикл таким образом, что все локальные переменные в этом кадре стека остаются активными (не собираются) до тех пор, пока не произойдет следующая сборка мусора.
оговорка вPythonВ . Все в примере находится в одной области видимости, поэтому переменнаяe
будет осуществляться за счетexcept
пункт удален. Ситуация отличается для функций с отдельными внутренними областями. Следующий пример иллюстрирует это:
def f(x):
del(x)
print(x)
x = 5
y = [5, 4, 3]
Output:
>>>f(x)
UnboundLocalError: local variable 'x' referenced before assignment
>>>f(y)
UnboundLocalError: local variable 'x' referenced before assignment
>>> x
5
>>> y
[5, 4, 3]
19. тип бул
True = False
if True == False:
print("I've lost faith in truth!")
Output:
I've lost faith in truth!
проиллюстрировать:исходный,Pythonнисколькоbool
Тип (люди используют 0 для ложных значений и ненулевые значения, такие как 1, для истинных значений). Позже они добавилиTrue
, False
, а такжеbool
тип, однако для обратной совместимости они не могутTrue
а такжеFalse
Установите константу, просто установите встроенную переменную.Python 3Поскольку обратная совместимость больше не требуется, это наконец-то можно исправить, поэтому этот пример нельзя использовать вPython 3.xв исполнении.
20. Ловушка метода append
some_list = [1, 2, 3]
some_dict = {
"key_1": 1,
"key_2": 2,
"key_3": 3
}
some_list = some_list.append(4)
some_dict = some_dict.update({"key_4": 4})
Output:
>>> print(some_list)
None
>>> print(some_dict)
None
проиллюстрировать:Большинство методов, которые изменяют объекты последовательности/карты, напримерlist.append
,dict.update
,list.sort
И так далее, все модифицируем объект на месте и возвращаемNone
, что повышает производительность за счет предотвращения создания копии объекта.