Комментарии 44
def func(first, second, *, kwonly):
def func(first, second, *, kwonly=True):
print(first, second, kwonly)
def func2(first, second, *args, kwonly=True):
print(first, second, *args, kwonly)
func(1) #-> TypeError: func() missing 1 required positional argument: 'second'
func(1, 2) #-> 1 2 True
func(1, 2, False) #-> TypeError: func() takes 2 positional arguments but 3 were given
# используя * в объявлении вы укажите, что
# ваша функция должна быть вызвана с двумя
# позиционными параметрами
func(1, 2, kwonly=False) #-> 1 2 False
func2(1, 2, False) #-> 1 2 False True
# *args заберёт в себя все позиционные
# параметры, то есть вашу функцию может будет
# вызывать с неограниченным (>2) числом
# параметров
Уже куплена неделю назад. :) И коллегам нравится. На Хабре как раз месяц назад статья была с отрывком из неё, так и нашёл.
А вот
collection |= []
# TypeError: unsupported operand type(s) for |=: 'NoneType' and 'list'
не работает
Судя по PEP, ввели не вариант, а новый механизм, потому что используя только or
and
можно было не всё сделать. Кроме того, что этими механизмами можно записывать условия в выражениях, они больше ничем не связаны. Собственно, варианты с or
и and
работают только потому, что эти бинарные операции работают лениво, и возвращают для or
: первый операнд, если он сводится к True, иначе — второй; для and
: первый операнд, если он — False, иначе второй.
item = (
items and item[0]
or can_use_others and other_items and other_items[0]
or default
)
Представьте себе такое выражение с тернарными операторами.
С другой стороны если развернуть это в блок кода, то будет читабельнее, с третьей стороны в лямбде блок кода не употребишь.
if x != x:
работает при обработке данных таблиц экселя, в других случаях не проверял.
Как и во многих языках в python 1 эквивалентно True, а 0 — False, то есть
1 == True.
На самом деле такая формулировка не точна.
Они равны (и поэтому функция хеширования дает одинаковый результат и Ваш пример со словарем корректен), но они не эквивалентны (то есть это разные объекты с разными id).
Проверка на проверка на равенство делается оператором ==
Проверка на эквивалентность делается оператором is
print(1 == True) # True
print(1 is True) # False
print(id(1)) # 10943008
print(id(True)) # 10262944
Просто использовать термин «равно», мне показалось тоже не хорошей идеей. Надо было выбрать другой термин.
bool — дочерний к int
Ой всё (tm)
Если человек не различает _эквивалентно_ и приведение типов в операции сравнения…
С днем знаний, чо
Значения по умолчанию ф-ция хранит в поле __defaults__, можно в любой момент узнать, что там находится.Похожий механизм есть и объектов. Сегодня буквально пишу
class Example:
arr=[]
__init__(self,...
И с удивлением обнаруживаю, что arr ведёт себя как static поле. От неожиданности в конструкторе прописал self.arr=[].copy. Вроде надёжно, хы :DПоэтому для «полей» класса действует такая же рекомендация, что и для аргументов функций со значением по умолчанию — не надо использовать изменяемые типы.
self.arr=[].copy
Судя по «трюкам» в этой статье не все поймут вашего сарказма в этом месте.
Поясню. Не стоило в конструкторе делать копирование. Литералы [], {}, () и т.д. идентичны соответствующим выражениям: list(), dict(), tuple().
Тело объявления класса выполняется один раз при первом импорте модуля или при запуске программы, если это __main__. Если вы употребите литерал [] или вызов list() в конструкторе, то список будет инициализироваться при каждом вызове конструктора. Кстати, __init__ — это, строго говоря, не конструктор. В питоне конструктор — это __new__. Это я так… на всякий случай.
Кстати о списках и прочих mutable объектах в классовых переменных. Это бывает полезно, если мы хотим без использования метаклассов реализовать класс, который будет «знать» все свои инстансы. Он просто будет сохранять их в свою коллекцию на классовом уровне.
class MyClass:
all_instances = []
def __init__(self):
self.all_instances.append(self)
instance1 = MyClass()
instance2 = MyClass()
print(MyClass.all_instances == [instance1, instance2]) # True
Кстати,__init__
— это, строго говоря, не конструктор. В питоне конструктор — это__new__
.
Неправда ваша. Метод __init__
полностью аналогичен конструкторам в других популярных языках (кроме Delphi который пошел своим путем).
Для вашего случая:
del instance1
del instance2
instance3 = MyClass()
print(instance3.all_instances) # [<__main__.MyClass object at 0xXXXXXXX1>, <__main__.MyClass object at 0xXXXXXXX2>, <__main__.MyClass object at 0xXXXXXXX3>]
даже после удаления всех объектов класса вся коллекция остается в памяти со всеми аттрибутами, созданными для каждого объекта.
Не удаляйте объекты. :)
А если серьёзно, то и так сложно придумать пример, когда такое может быть полезно (вот для форм или эмуляции физических систем), вряд ли мы захотим удалять такие объекты, а если и захотим, то удалять скорее всего будем с помощью метода класса.
Я бы вообще переменные отдельных экземпляров не хранил в таких случаях:
def init_system():
Class(1)
Class(2)
Class(3)
Но метод потенциально опасный, нужно весьма аккуратно подходить к подобным реализациям.
SEX = 'Female', 'Male'
sex = SEX[True] # Male
sex = SEX[False] # Female
Справедливости ради, про создание словарей с простыми строковыми ключами через конструктор dict()
упоминается в официальном учебнике (последний абзац):
Другим полезным встроенным в Python типом данных является словарь (смотрите раздел Типы сопоставлений — dict). В других языках словари иногда называются «ассоциативной памятью» или «ассоциативными массивами». В отличие от последовательностей, индексируемых диапазоном чисел, словари индексируются ключами, которые могут быть любым неизменяемым типом; строки и числа ключами могут быть всегда. Кортежи могут использоваться в качестве ключей, только если они содержат строки, числа или кортежи; если кортёж прямо или косвенно содержит любой изменяемый объект, он не может использоваться в качестве ключа. Вы не можете использовать в качестве ключей списки, поскольку они могут быть изменены по месту с помощью присваиваний по индексам, срезам или такими методами, как append()
или extend()
.
Лучше всего думать о словаре как о наборе пар ключ: значение, с тем требованием, что ключи должны быть уникальными (в пределах одного словаря). Пара фигурных скобок создаёт пустой словарь: {}
. Размещение в фигурных скобках списка разделённых запятыми пар ключ: значение добавляет начальные пары ключ: значение в словарь; также таким способом словари пишутся при их выводе на печать.
Основными операциями над словарём являются сохранение значения по некоторому ключу и извлечение значения по указанному ключу. Также с помощью инструкции del
пару ключ: значение можно удалить. Если вы сохраняете значение по ключу, который уже используется, старое связанное с этим ключом значение будет забыто. При попытке извлечения значения по несуществующему ключу возбуждается ошибка.
Выполнение на словаре выражения list(d)
вернёт список всех используемых в словаре ключей в порядке их вставки (если вы хотите, чтобы он был отсортирован, просто используйте вместо него sorted(d)
). Чтобы проверить, находится ли в словаре один ключ, используйте ключевое слово in
.
Ниже приведён небольшой пример использования словаря:
>>> tel = {'jack': 4098, 'sape': 4139}
>>> tel['guido'] = 4127
>>> tel
{'jack': 4098, 'sape': 4139, 'guido': 4127}
>>> tel['jack']
4098
>>> del tel['sape']
>>> tel['irv'] = 4127
>>> tel
{'jack': 4098, 'guido': 4127, 'irv': 4127}
>>> list(tel)
['jack', 'guido', 'irv']
>>> sorted(tel)
['guido', 'irv', 'jack']
>>> 'guido' in tel
True
>>> 'jack' not in tel
False
Конструктор dict()
строит словари непосредственно из последовательностей пар ключ-значение:
>>> dict([('sape', 4139), ('guido', 4127), ('jack', 4098)])
{'sape': 4139, 'guido': 4127, 'jack': 4098}
Кроме того, для создания словарей из произвольных выражений для ключей и значений можно использовать словарные включения:
>>> {x: x**2 for x in (2, 4, 6)}
{2: 4, 4: 16, 6: 36}
Если ключи являются простыми строками, иногда проще указывать пары с помощью именованных аргументов:
>>> dict(sape=4139, guido=4127, jack=4098)
{'sape': 4139, 'guido': 4127, 'jack': 4098}
Это я к тому, что изучая особенности языка только на ходу очень много базовых, но полезных «штук», проходит мимо. Имхо, лучше систематически подходить к такому вопросу и начать с учебника, тогда, например, распаковка кортежей и создание функций с возможностью передачи аргументов только по ключу не будет сюрпризом.
Собственно статья для тех, у кого нет недели на чтение книги, но есть время на чтение статейки другой.
Но материала ещё много, надеюсь в последующих статьях вы тоже узнаете что-то новое.
Я поставил стандартное значение равным пустому массиву, который я в теле функции заполнял значениями и как-то использовал. Причём функция была ещё и в добавок рекурсивная. В общем, это было крайне неприятной находкой.
С параметрами по умолчанию связана одна интересная особенность: они вычисляются на этапе компиляции модуля в байт-код
Это не так. Определения функций (и классов) "вычисляются" на этапе выполнения. Хотя я не уверен, что имеется в виду под этапом компиляции в байт-код. Насколько я понимаю такого "этапа" нет.
class HelloPrinter(list):
def __init__(self):
print('Hello')
super().__init__()
def func(el, collection=HelloPrinter()):
collection.append(el)
print(collection)
print('Plain print')
func('a')
func('b')
def func(el, collection=HelloPrinter()):
collection.append(el)
print(collection)
func('c')
func('d')
➜ ~ python3 test_default_func.py
Hello
Plain print
['a']
['a', 'b']
Hello
['c']
['c', 'd']
Побежал, поставил 3.7 только по причине f-строк, просто кайф!
Вот этот момент:
# используя * в объявлении вы укажите, что
# ваша функция должна быть вызвана с двумя
# позиционными параметрами
Не совсем clear.
То есть ИМХО: все что до * будут позиционными а все что после * будут ключевыми
def a(foo, *, bar):
print(f"foo:{foo}, bar:{bar}")
a(100, bar=200)
В вашем примере вы должны будете вызывать ф-цию именно так, вы её и вызвали, то есть передав первый аргумент по ключу или без него и второй только без ключа, если передать только один параметр или передать три, то программа упадёт.
print(" ".join(str(i) for i in a))
Можно же использовать map
''.join(map(str, [1, 2, 3, 4, 5]))
'12345'
А если надо, то можно использовать лямбда функцию
' '.join(map(lambda x: str(x**2), [1, 2, 3, 4, 5]))
'1 4 9 16 25'
Интересности и полезности python