class Some:
def __hash__(self):
return 1
def __eq__(self, other):
return False
d = {Some() for _ in range(5)}
print(len(d))
Что выведет этот код?
Может показаться, что ответ = 1, т.к. hash любого инстанса класса Some равен 1.
1) При добавлении нового элемента в set, dict питон находит его hash(element) (если вы не знаете, что такое хэш функция и зачем она нужна, почитайте про хэш таблицы).
2) Из этого хэша определяется индекс в массиве, куда нужно вставить этот элемент.
3) Если по этому индексу ничего нет, то элемент просто вставляется.
4) А если там уже есть какой-то элемент (это коллизия), то питон уже сравнивает существуещий элемент с тем, который хотим добавить (new == old). Если они равны, то старый заменяется новым элементом.
5) В нашем случае все экземпляры Some разные, т.к.
__eq__ всегда возвращает False
Поэтому ответ 5.
#set #dict #hash
Что выведет этот код?
В python циклы использует тот же scope (область видимости), в которой они объявлены и оставляют переменную после себя.
Почему не 0 и не 7?
Не 7, потому что range(stop) не включая stop, т.е. дойдет до 7-1 = 6.
Не 0, потому что цикл полностью отработал, т.е. дошел до 6.
Поэтому ответ 6.
#forloop
В python циклы использует тот же scope (область видимости), в которой они объявлены и оставляют переменную после себя.
Почему не 0 и не 7?
Не 7, потому что range(stop) не включая stop, т.е. дойдет до 7-1 = 6.
Не 0, потому что цикл полностью отработал, т.е. дошел до 6.
Поэтому ответ 6.
#forloop
Что выведет этот код?
Реализация функции
Реализация функции
allэквивалентна этому:
def all(iterable):- True, потому что iterable пустой список и мы не заходим в цикл.
for element in iterable:
if not element:
return False
return True
all([])
all([[]])- False, потому что мы заходим в цикл и первый элемент iterable - пустой список, а
bool([]) = False, потому что первый элемент это уже не пустой список, а список в котором пустой список, т.е.
all([[[]]]) - True
bool([[]]) = True
Что выведет этот код?
Когда мы объявляем какую то последовательность, где элементы идут через запятую и без явных скобок, то создается tuple:
А функция sorted принимает любой iterable и всегда возврает
А кортеж (1, 2, 3, 4) не равен списку [1, 2, 3, 4], даже если значения равны.
Когда мы объявляем какую то последовательность, где элементы идут через запятую и без явных скобок, то создается tuple:
nums = 1, 2, 3, 4
type(nums) # <class 'tuple'>
А функция sorted принимает любой iterable и всегда возврает
list.
А кортеж (1, 2, 3, 4) не равен списку [1, 2, 3, 4], даже если значения равны.
Что выведет этот код?
Ответ: 1.
Потому что все строки-литералы разделенные любым количеством пробелов соединяются в одну.
То есть:
то же самое, что и
Это работает и с одинарными, двойными, тройными кавычками.
Однако это работает только с литералами.
В итоге у нас список из одного элемента:
#strings #строки
Ответ: 1.
Потому что все строки-литералы разделенные любым количеством пробелов соединяются в одну.
То есть:
"Hello" "World"
то же самое, что и
"HelloWorld"
Это работает и с одинарными, двойными, тройными кавычками.
x = 'One' "Two" '''Three'''
print(x)
# OneTwoThreeОднако это работает только с литералами.
s1 = "String1"
s2 = "String2"
print(s1 s2)
# SyntaxError: invalid syntax.В итоге у нас список из одного элемента:
["AppleBananaOrangePineapple"]
#strings #строки
Что выведет этот код?
Строки длиной до 4096 (до версии 3.7 длиной до 21) кэшируются.
Строки не кэшируются, если они созданы в рантайме:
Так же следует понимать, что это может поменяться в любой момент и лучше сравнивать строки через ==, а не через is.
#string #строки
Строки длиной до 4096 (до версии 3.7 длиной до 21) кэшируются.
s1 = "a" * 4096
s2 = "a" * 4096
print(s1 is s2) # True
s1 = "a" * 4097
s2 = "a" * 4097
print(s1 is s2) # False
Строки не кэшируются, если они созданы в рантайме:
s1 = "python"
s2 = "".join(["p", "y", "t", "h", "o", "n"]) # выражение выполняется в рантайме
print(s1 is s2) # False
Так же следует понимать, что это может поменяться в любой момент и лучше сравнивать строки через ==, а не через is.
#string #строки
Что выведет этот код?
hash() - функция, которая возвращает хэш-значение объекта, если объект хэшируемый (имеет __hash__).
Хэш-значение это всегда целое число.
Хэш-значение целых чисел есть само целое число. Но в зависимости от разрядности платформы. Если у Вас 64-битовая платформа, то hash() вернет значение от -2^63 до 2^63-1.
Хорошо, что насчет нашего кода?
True - имеет hash = 1
1 - int в допустимом диапазоне, поэтому hash() вернет его же.
И для всех чисел, которые равны какому то целому числу, хэш функция вернет это целое число:
А в классе Item мы переопределили два метода:
и
В итоге получается, что все ключи равны и у всех ключей одно и то же хэш-значение.
Поэтому ответ 1.
#hash #числа
hash() - функция, которая возвращает хэш-значение объекта, если объект хэшируемый (имеет __hash__).
Хэш-значение это всегда целое число.
Хэш-значение целых чисел есть само целое число. Но в зависимости от разрядности платформы. Если у Вас 64-битовая платформа, то hash() вернет значение от -2^63 до 2^63-1.
Хорошо, что насчет нашего кода?
True - имеет hash = 1
print(hash(True)) # 1
print(isinstance(True, int)) # True
print(True == 1) # True
1 - int в допустимом диапазоне, поэтому hash() вернет его же.
И для всех чисел, которые равны какому то целому числу, хэш функция вернет это целое число:
print(1 == 1.0) # True
print(hash(1.0)) # 1
А в классе Item мы переопределили два метода:
__hash()__
-> 1и
__eq__
-> 1 == other
item = Item()
print(1 == item) # True
print(hash(item)) # 1
В итоге получается, что все ключи равны и у всех ключей одно и то же хэш-значение.
Поэтому ответ 1.
#hash #числа
Что выведет этот код?
Когда мы выполняем операции вида:
Вызывается метод
В нашем случае:
А
В итоге принтанется:
#числа
Когда мы выполняем операции вида:
left + right
Вызывается метод
left.__add__
, но если left.__add__
возвращает NotImplemented
, то вызываетсяright.__radd__
, а если и этот метод возвращает NotImplemented
, то выбрасывается ошибка TypeError
.В нашем случае:
x = MyInt
и мы переопределили метод __add__
, принтанется int __add__
, но далее мы возвращаем NotImplemented
что заставляет вызвать метод MyFloat.__radd__
.А
MyFloat.__add__
не будет вызыван, т.к. всегда вызывается __add__
левого операнда и только __radd__
правого соответсвенно.В итоге принтанется:
int __add__
float __radd__
10.0
#числа
Set - это множество, в котором элементы не имеют порядок.
Т.е. порядок не гарантируется, но порядок в них не случайный и зная детали реализации можно предположить в каком порядке они будут выведены (при итерации).
Так почему же в нашем случае элементы будут отсортированы?
В множестве элементы лежат не в случайном порядке, а в порядке возрастания хэш-значения % длина списка.
Как работает set?
Внутри мы имеем тот же список и мы так же хотим получать элементы по индексам (потому что это быстро).
Но как получить индекс?
Берем hash(element) чтобы получить хэш-значение элемента, у нас это int, а как мы ранее выяснили hash(int) -> int.
Далее, чтобы получить индекс мы берем остаток от деления этого хэш-значения на длину (реальную, а не заполненную) списка внутри сета.
Когда мы объявляем пустое множество, то длина по умолчанию равна 8.
Т.е. если мы объявим такой сет, то он сохранится грубо говоря в таком виде:
А чтобы преобразовать все set в list, мы должны пробежаться по всем индексам и вытащить оттуда не NULL значения.
Вот и получается, что:
Это работает только с числами, которые меньше длины списка. Допустим у нас:
Никогда не надейтесь на порядок в множествах, этот код чисто для примера и объяснения.
Начальный размер множества (списка под копотом) 8, но как только мы заполняем 2/3 от 8, то размер увеличивается в 4 раза, а точнее.
В 4 раза - если элементов в множестве меньше 50000.
В 2 раза - если больше 50000.
Все эти параметры и числа могут меняться в будущем.
#set
Т.е. порядок не гарантируется, но порядок в них не случайный и зная детали реализации можно предположить в каком порядке они будут выведены (при итерации).
Так почему же в нашем случае элементы будут отсортированы?
В множестве элементы лежат не в случайном порядке, а в порядке возрастания хэш-значения % длина списка.
Как работает set?
Внутри мы имеем тот же список и мы так же хотим получать элементы по индексам (потому что это быстро).
Но как получить индекс?
Берем hash(element) чтобы получить хэш-значение элемента, у нас это int, а как мы ранее выяснили hash(int) -> int.
Далее, чтобы получить индекс мы берем остаток от деления этого хэш-значения на длину (реальную, а не заполненную) списка внутри сета.
Когда мы объявляем пустое множество, то длина по умолчанию равна 8.
Т.е. если мы объявим такой сет, то он сохранится грубо говоря в таком виде:
s = {2, 3, 1, 5}
# [NULL, 1, 2, 3, NULL, 5, NULL, NULL]
# 2 - hash(2) = 2, 2 % 8 = 2 (индекс в списке)
# 3 - hash(3) = 3, 3 % 8 = 3 (индекс в списке)
# 1 - hash(1) = 1, 1 % 8 = 1 (индекс в списке)
# 5 - hash(5) = 5, 5 % 8 = 5 (индекс в списке)
А чтобы преобразовать все set в list, мы должны пробежаться по всем индексам и вытащить оттуда не NULL значения.
Вот и получается, что:
list(s) == [1, 2, 3, 8] # True
Это работает только с числами, которые меньше длины списка. Допустим у нас:
s = {2, 3, 1, 8}
# [8, 1, 2, 3, NULL, NULL, NULL, NULL]
# 2 - hash(2) = 2, 2 % 8 = 2 (индекс в списке)
# 3 - hash(3) = 3, 3 % 8 = 3 (индекс в списке)
# 1 - hash(1) = 1, 1 % 8 = 1 (индекс в списке)
# 8 - hash(8) = 8, 8 % 8 = 0 (индекс в списке)
list(s) == [1, 2, 3, 8] # уже False
Никогда не надейтесь на порядок в множествах, этот код чисто для примера и объяснения.
Начальный размер множества (списка под копотом) 8, но как только мы заполняем 2/3 от 8, то размер увеличивается в 4 раза, а точнее.
В 4 раза - если элементов в множестве меньше 50000.
В 2 раза - если больше 50000.
Все эти параметры и числа могут меняться в будущем.
#set
Что выведет этот код?
Могло показаться, что выведется словарь с буквами и их повторением в словаре.
То есть мы просто каждой букве в слове поставили значение counter.get(char, 1) + 1 = 2.
Чтобы посчитать реальный счетчик повторении есть несколько способов:
#dict #counter
Могло показаться, что выведется словарь с буквами и их повторением в словаре.
dict.setdefault(key, default=None)
- возвращает значение по ключу ke
y, а если ключа нет в словаре, то добавляет пару key=defaul
t и возвращает значение default.dict.get(key, default=None)
- возвращает значение по ключу ke
y, а если ключа нет, то возвращает defaul
t не добавляя пару в словарь.То есть мы просто каждой букве в слове поставили значение counter.get(char, 1) + 1 = 2.
Чтобы посчитать реальный счетчик повторении есть несколько способов:
s = "hello"
# 1
counter = {}
for char in s:
counter[char] = counter.setdefault(char, 0) + 1
# 2
counter = {}
for char in s:
counter[char] = counter.get(char, 0) + 1
# 3
from collections import defaultdict
counter = defaultdict(int)
for char in s:
counter[char] += 1
# 4
from collections import Counter
counter = Counter(s)
#dict #counter
Что выведет этот код?
Iterable - это объект по которому можно пройтись. Для питона это любой объект у которого есть метод
Чтобы преобразовать какой то объект в
Первым делом смотриться метод
Но если у объекта нет метода
В метод
Для чего это нужно?
На самом деле это очень старый механизм и в реальности почти не нужный. Так называемая "legacy feature".
Но может быть полезен, когда у вас есть какой то объект и вы хотите как то проитерироваться по нему.
#iter #getitem
Iterable - это объект по которому можно пройтись. Для питона это любой объект у которого есть метод
__iter__
или __getitem__
.Чтобы преобразовать какой то объект в
list
, нужно проитерироваться по объекту, т.е. вызвать iter(iterable)
.Первым делом смотриться метод
__iter__
, если он возвращает iterator
, то переберая все элементы итератора строиться list
.Но если у объекта нет метода
__iter__
, но есть метод __getitem__
, то создается iterator
на основе этого метода. В метод
__getitem__
по очередно передаются индексы (просто числа) от 0 до момента пока __getitem__
не выбросит ошибку StopIteration
или IndexError
.Для чего это нужно?
На самом деле это очень старый механизм и в реальности почти не нужный. Так называемая "legacy feature".
Но может быть полезен, когда у вас есть какой то объект и вы хотите как то проитерироваться по нему.
class Library:
def __init__(self, books: list):
self.books = books
def __getitem__(self, index):
return self.books[index]
for book in Library(["a", "b", "c"]):
print(book)
#iter #getitem
Как можно вызывать эту функцию?
Правильный ответ # 1 и # 2.
В списке аргументов функции:
- все аргументы которые стоят до
- все аргументы после
- после
Правильный ответ # 1 и # 2.
arg1
- только позиционный аргумент.arg2
- можно передать как поцизионно, так и по имени.arg3
- только как именованный.В списке аргументов функции:
- все аргументы которые стоят до
/
должны передаваться как позиционные.- все аргументы после
*
должны передаваться как именованные.- после
/
и до *
можно передавать как если бы не было этих символов.