Сколько стоят операции над list, set и dict в python? разбираемся с временной сложностью

@classmethod и @staticmethod

Я не пользовался ими ранее, так что сделал небольшое исследование.

  • Декоратор <*@classmethod>* может быть вызван при помощи экземпляра класса, или напрямую, через собственный класс Python в качестве первого аргумента. В соответствии с документацией Python: он может быть вызван как в классе (например, C.f()), или в экземпляре (например, C().f()). Экземпляр игнорируется, за исключением его класса. Если метод класса вызван для выведенного класса, то объект выведенного класса передается в качестве подразумеваемого первого аргумента.
  • Декоратор @classmethod, в первую очередь, используется как чередуемый конструктор или вспомогательный метод для инициализации.
  • Декоратор <*@staticmethod>* — это просто функция внутри класса. Вы можете вызывать их обоих как с инициализацией класса так и без создания экземпляра класса. Обычно это применяется в тех случаях, когда у вас есть функция, которая, по вашему убеждению, имеет связь с классом. По большей части, это выбор стиля.

Если мы взглянем на пример кода, в котором показано, как работает декоратор, это может помочь понять основные принципы:

Python

# -*- coding: utf-8 -*-

class DecoratorTest(object):
«»»
Тестируем обычный метод против @classmethod против @staticmethod
«»»
def __init__(self):
«»»Конструктор»»»
pass

def doubler(self, x):
print(«умножаем на 2»)
return x*2

@classmethod
def class_tripler(klass, x):
print(«умножаем на 3: %s» % klass)
return x*3

@staticmethod
def static_quad(x):
print(«умножаем на 4»)
return x*4

if __name__ == «__main__»:
decor = DecoratorTest()
print(decor.doubler(5))
print(decor.class_tripler(3))
print(DecoratorTest.class_tripler(3))
print(DecoratorTest.static_quad(2))
print(decor.static_quad(3))

print(decor.doubler)
print(decor.class_tripler)
print(decor.static_quad)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

# -*- coding: utf-8 -*-
 

classDecoratorTest(object)

«»»

    Тестируем обычный метод против @classmethod против @staticmethod
    «»»

def__init__(self)

«»»Конструктор»»»

pass

defdoubler(self,x)

print(«умножаем на 2»)

returnx*2

@classmethod

defclass_tripler(klass,x)

print(«умножаем на 3: %s»%klass)

returnx*3

@staticmethod

defstatic_quad(x)

print(«умножаем на 4»)

returnx*4

if__name__==»__main__»

decor=DecoratorTest()

print(decor.doubler(5))

print(decor.class_tripler(3))

print(DecoratorTest.class_tripler(3))

print(DecoratorTest.static_quad(2))

print(decor.static_quad(3))

print(decor.doubler)

print(decor.class_tripler)

print(decor.static_quad)

Этот пример демонстрирует, что вы можете вызывать обычный метод и оба метода декоратора одним и тем же путем

Обратите внимание на то, что вы можете вызывать обе функции @classmethod и @staticmethod прямо из класса или из экземпляра класса. Если вы попытаетесь вызвать обычную функцию при помощи класса (другими словами, DecoratorTest.doubler(2)), вы получите ошибку TypeError

Также стоит обратить внимание на то, что последний оператор вывода показывает, что decor.static_quad возвращает обычную функцию вместо связанного метода.

Other Set Operations

We can test if an item exists in a set or not, using the keyword.

Output

True
False

Built-in Functions with Set

Built-in functions like , , , , , , , etc. are commonly used with sets to perform different tasks.

Function Description
all() Returns if all elements of the set are true (or if the set is empty).
any() Returns if any element of the set is true. If the set is empty, returns .
enumerate() Returns an enumerate object. It contains the index and value for all the items of the set as a pair.
len() Returns the length (the number of items) in the set.
max() Returns the largest item in the set.
min() Returns the smallest item in the set.
sorted() Returns a new sorted list from elements in the set(does not sort the set itself).
sum() Returns the sum of all elements in the set.

5.6. Looping Techniques¶

When looping through dictionaries, the key and corresponding value can be
retrieved at the same time using the method.

>>> knights = {'gallahad' 'the pure', 'robin' 'the brave'}
>>> for k, v in knights.items():
...     print(k, v)
...
gallahad the pure
robin the brave

When looping through a sequence, the position index and corresponding value can
be retrieved at the same time using the function.

>>> for i, v in enumerate():
...     print(i, v)
...
0 tic
1 tac
2 toe

To loop over two or more sequences at the same time, the entries can be paired
with the function.

>>> questions = 'name', 'quest', 'favorite color'
>>> answers = 'lancelot', 'the holy grail', 'blue'
>>> for q, a in zip(questions, answers):
...     print('What is your {0}?  It is {1}.'.format(q, a))
...
What is your name?  It is lancelot.
What is your quest?  It is the holy grail.
What is your favorite color?  It is blue.

To loop over a sequence in reverse, first specify the sequence in a forward
direction and then call the function.

>>> for i in reversed(range(1, 10, 2)):
...     print(i)
...
9
7
5
3
1

To loop over a sequence in sorted order, use the function which
returns a new sorted list while leaving the source unaltered.

>>> basket = 'apple', 'orange', 'apple', 'pear', 'orange', 'banana'
>>> for i in sorted(basket):
...     print(i)
...
apple
apple
banana
orange
orange
pear

Using on a sequence eliminates duplicate elements. The use of
in combination with over a sequence is an idiomatic
way to loop over unique elements of the sequence in sorted order.

>>> basket = 'apple', 'orange', 'apple', 'pear', 'orange', 'banana'
>>> for f in sorted(set(basket)):
...     print(f)
...
apple
banana
orange
pear

It is sometimes tempting to change a list while you are looping over it;
however, it is often simpler and safer to create a new list instead.

Frozenset в Python

Frozenset (замороженное множество) – это класс с характеристиками множества, однако, как только элементы становятся назначенными, их нельзя менять. Кортежи могут рассматриваться как неизменяемые списки, в то время как frozenset-ы — как неизменные множества.

Множества являются изменяемыми и нехешируемыми, это значит, что мы не можем использовать их как словарные ключи. Замороженные множества (frozenset) являются хешированными и могут использоваться в качестве ключей словаря.

Для создания замороженного множества, мы используем метод . Давайте создадим два замороженных множества, Х и Y:

Python

X = frozenset()
Y = frozenset()

print(X)
print(Y)

1
2
3
4
5

X=frozenset(1,2,3,4,5,6)

Y=frozenset(4,5,6,7,8,9)

print(X)

print(Y)

Результат:

Python

frozenset({1, 2, 3, 4, 5, 6})
frozenset({4, 5, 6, 7, 8, 9})

1
2

frozenset({1,2,3,4,5,6})

frozenset({4,5,6,7,8,9})

Замороженные множества поддерживают использование множественных методов Python, таких как , , , , , , и .

Обход множеств в цикле

Множества так же как последовательности (строки, списки и т.д) и отображения (словари) являются коллекциями, т.е. могут хранить сложные структуры данных (например, кортежи). Но так как элементы множеств не снабжены никакими идентификаторами (ни индексами, ни ключами), то множества не поддерживают операторы извлечения среза и извлечения элемента по ключу . Тем не менее множества поддерживают оператор проверки на вхождение (а так же ) и функцию определения размера :

Множества поддерживают механизм итерирования и их элементы могут быть получены перебором в цикле :

Чаще всего множества используются для итерирования уникальных элементов последовательностей. И тут возможны два случая. Давайте рассмотрим следующий список:

В первом случае, может быть неважно в каком порядке обрабатываются элементы, главное обрабатывать только уникальные:

Во втором случае, по прежнему нужно обрабатывать только уникальные элементы, но порядок следования элементов в списке может оказаться важен. В этом случае используют пустые множества:

Так же множества могут выступать в роли итерируемого объекта в любых генераторах:

combinations_with_replacement(iterable, r)

Итератор под названием combinations_with_replacement очень похож на combinations. Единственная разница в том, что он создает комбинации повторяемых элементов. Давайте посмотрим на пример из предыдущего раздела:

Python

from itertools import combinations_with_replacement

for item in combinations_with_replacement(‘WXYZ’, 2):
print(».join(item))

1
2
3
4

fromitertoolsimportcombinations_with_replacement

foritem incombinations_with_replacement(‘WXYZ’,2)

print(».join(item))

Результат:

Python

WW
WX
WY
WZ
XX
XY
XZ
YY
YZ
ZZ

1
2
3
4
5
6
7
8
9
10

WW
WX
WY
WZ
XX
XY
XZ
YY
YZ
ZZ

Как вы видите, у нас есть четыре объекта в выдаче: WW, XX, YY и ZZ.

Как лучше выбирать элементы из списка?

Если вы хотите продуктивно работать со списками, то должны уметь получать доступ к данным, хранящимся в них.

Обычно мы получаем доступ к элементам списков, чтобы изменять определенные значения, обновлять или удалять их, или выполнять какие-либо другие операции с ними. Мы получаем доступ к элементам списков и, собственно, ко всем другим типам последовательностей, при помощи оператора индекса . Внутри него мы помещаем целое число.

# Выбираем первый элемент списка
oneZooAnimal = biggerZoo
# Выводим на экран переменную `oneZooAnimal`
print(oneZooAnimal)

Запустите данный код и убедитесь, что вы получите первый элемент списка, сохраненного в переменную . Это может быть поначалу несколько непривычно, но нумерация начинается с числа , а не .

Как получить последний элемент списка?

Ответ на этот вопрос является дополнением к объяснению в предыдущем разделе.

Попробуйте ввести отрицательное значение, например,  или , в оператор индекса, чтобы получить последние элементы нашего списка !

# Вставляем -1 
monkeys = biggerZoo
print(monkeys)
# А теперь -2
zebra = biggerZoo
print(zebra)

Не правда ли, не слишком сложно?

Что означает ошибка «Index Out Of Range»?

Эта ошибка одна из тех, которые вы будете видеть достаточно часто, особенно если вы новичок в программировании.

Лучший способ понять эту ошибку — попробовать ее получить самостоятельно.

Возьмите ваш список и передайте в оператор индекса либо очень маленькое отрицательное число, либо очень большое положительное число.

Как видите, вы можете получить ошибку «Индекс вне диапазона» в случаях, когда вы передаете в оператор индекса целочисленное значение, не попадающее в диапазон значений индекса списка. Это означает, что вы присваиваете значение или ссылаетесь на (пока) несуществующий индекс.

Срезы в списках

Если вы новичок в программировании и в Python, этот вопрос может показаться одним из наиболее запутанных.

Обычно нотация срезов используется, когда мы хотим выбрать более одного элемента списка одновременно. Как и при выборе одного элемента из списка, мы используем двойные скобки. Отличие же состоит в том, что теперь мы еще используем внутри скобок двоеточие. Это выглядит следующим образом:

# Используем нотацию срезов
someZooAnimals = biggerZoo
# Выводим на экран то, что мы выбрали
print(someZooAnimals)
# Теперь поменяем местами 2 и двоеточие
otherZooAnimals = biggerZoo
# Выводим на экран полученный результат
print(otherZooAnimals)

Вы можете видеть, что в первом случае мы выводим на экран список  начиная с его элемента , который имеет индекс . Иными словами, мы начинаем с индекса  и идем до конца списка, так как другой индекс не указан.

Что же происходит во втором случае, когда мы поменяли местами индекс  и двоеточие? Вы можете видеть, что мы получаем список из двух элементов, и . В данном случае мы стартуем с индекса  и доходим до индекса  (не включая его). Как вы можете видеть, результат не будет включать элемент .

В общем, подводя итоги:

# элементы берутся от start до end (но элемент под номером end не входит в диапазон!)
a
# элементы берутся начиная со start и до конца
a    
# элементы берутся с начала до end (но элемент под номером end не входит в диапазон!)
a

Совет: передавая в оператор индекса только двоеточие, мы создаем копию списка.

В дополнение к простой нотации срезов, мы еще можем задать значение шага, с которым будут выбираться значения. В обобщенном виде нотация будет иметь следующий вид:

# Начиная со start, не доходя до end, с шагом step
a

Так что же по сути дает значение шага?

Ну, это позволяет вам буквально шагать по списку и выбирать только те элементы, которые включает в себя значение вашего шага. Вот пример:

Обратите внимание, что если вы не указали какое-либо значение шага, оно будет просто установлено в значение . При проходе по списку ни один элемент пропущен не будет

Также всегда помните, что ваш результат не включает индекс конечного значения, который вы указали в записи среза!

Как случайным образом выбрать элемент из списка?

Для этого мы используем пакет .

# Импортируем функцию `choice` из библиотеки `random` 
from random import choice
# Создадим список из первых четырех букв алфавита
list = 
# Выведем на экран случайный элемент списка
print(choice(list))

Если мы хотим выбрать случайный элемент из списка по индексу, то можем использовать метод  из той же библиотеки .

# Импортируем функцию `randrange` из библиотеки `random`
from random import randrange
# Создадим список из первых четырех букв алфавита
randomLetters = 
# Выбираем случайный индекс нашего списка
randomIndex = randrange(0,len(randomLetters))
# Выводим случайный элемент на экран
print(randomLetters)

Совет: обратите внимание на библиотеку , она может вам пригодиться во многих случаях при программировании на Python

Топ-11 высокодоходных ниш Инстаграм на 2020 год

Список (list)

Для начала вспомним операции работы со списками.

Операция Пример Трудоемкость Замечания
Взятие индекса l O(1)  
Сохранение элемента l = 0 O(1)  
Длина len(l) O(1)  
Добавление в конец l.append(5) O(1)  
Извлечение с конца l.pop() O(1)  
Очистка списка l.clear() O(1) Аналогично l = []
Срез(Slice) l O(b-a)  
Расширение l.extend(A) O(len(A)) Зависит только от длины A
Создание list(A) O(len(A)) Зависит от длины A (итерируемый объект)
Проверка ==, != l1 == l2 O(N)  
Присваивание в срез = … O(N)  
Удаление элемента del l O(N)  
Поиск элемента x (not) in l O(N) Поиск работает за O(N)
Копирование списка l.copy() O(N) То же самое что l, который O(N)
Удаление из списка l.remove(..) O(N)  
Извлечение элемента l.pop(i) O(N) O(N-i): l.pop(0):O(N) (см. выше)
Экстремумы min(l)/max(l) O(N) Поиск работает за O(N)
Обращение l.reverse() O(N)  
Итерирование for v in l: O(N)  
Сортировка l.sort() O(N Log N)  
Перемножение k*l O(k N) 5*l будет за O(N), len(l)*l будет O(N**2)

У разработчиков типа данных list Python было много вариантов каким сделать его во время реализации. Каждый выбор повлиял на то, как быстро список мог выполнять операции. Одно из решений было сделать список оптимальным для частых операций.

Две частые операции — индексирование и присваивание на позицию индекса. В списках Python значения присваиваются и извлекаются из определенных известных мест памяти. Независимо от того, насколько велик список, индексный поиск и присвоение занимают постоянное количество времени и, таким образом их трудоемкость O(1).

Извлечение элемента(pop) из списка Python по умолчанию выполняется с конца, но, передавая индекс, вы можете получить элемент из определенной позиции. Когда pop вызывается с конца, операция имеет сложность O(1) , а вызов pop из любого места — O(n). Откуда такая разница?

Когда элемент берется из середины списка Python, все остальные элементы в списке сдвигаются на одну позицию ближе к началу. Это суровая плата за возможность брать индекс за O(1), что является более частой операцией.

По тем же причинам вставка в индекс — O(N); каждый последующий элемент должен быть сдвинут на одну позицию ближе к концу, чтобы разместить новый элемент. Неудивительно, что удаление ведет себя таким же образом.

Итерирование выполняется за O(N), потому что для итерации по N элементам требуется N шагов. Это также объясняет, почему оператор in, max, min в Python является O(N): чтобы определить, находится ли элемент в списке, мы должны перебирать каждый элемент.

Чтобы получить доступ к фрагменту списка, мы должны перебрать каждый элемент между индексами a и b. Таким образом, доступ к срезу — O(k), где k — размер среза. Удаление среза O(N) по той же причине, что удаление одного элемента — O(N): N последующих элементов должны быть смещены в сторону начала списка.

Чтобы понять умножение списка на целое k, вспомним, что конкатенация выполняется за O(M), где M — длина добавленного списка. Из этого следует, что умножение списка равно O(N k), так как умножение k-размера списка N раз потребует времени k (N-1).

Замена сеттеров и геттеров на свойство Python

Давайте представим, что у нас есть код, который написал кто-то, кто не очень понимает Python. Как и я, вы скорее всего, видели такого рода код ранее:

Python

# -*- coding: utf-8 -*-
from decimal import Decimal

class Fees(object):
«»»»»»
def __init__(self):
«»»Конструктор»»»
self._fee = None

def get_fee(self):
«»»
Возвращаем текущую комиссию
«»»
return self._fee

def set_fee(self, value):
«»»
Устанавливаем размер комиссии
«»»
if isinstance(value, str):
self._fee = Decimal(value)
elif isinstance(value, Decimal):
self._fee = value

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# -*- coding: utf-8 -*-

fromdecimalimportDecimal

classFees(object)

«»»»»»

def__init__(self)

«»»Конструктор»»»

self._fee=None

defget_fee(self)

«»»

        Возвращаем текущую комиссию
        «»»

returnself._fee

defset_fee(self,value)

«»»

        Устанавливаем размер комиссии
        «»»

ifisinstance(value,str)

self._fee=Decimal(value)

elifisinstance(value,Decimal)

self._fee=value

Для использования этого класса, нам нужно использовать сеттеры и геттеры, которые определены как:

Python

f = Fees()
f.set_fee(«1»)

print(f.get_fee()) # Decimal(‘1’)

1
2
3
4

f=Fees()

f.set_fee(«1»)

print(f.get_fee())# Decimal(‘1’)

Если вам нужно добавить обычную точечную нотацию атрибутов в данный код без выведения из строя всех приложений в этой части кода, вы можете сделать это очень просто, добавив свойство:

Python

# -*- coding: utf-8 -*-
from decimal import Decimal

class Fees(object):
«»»»»»
def __init__(self):
«»»Конструктор»»»
self._fee = None

def get_fee(self):
«»»
Возвращаем текущую комиссию
«»»
return self._fee

def set_fee(self, value):
«»»
Устанавливаем размер комиссии
«»»
if isinstance(value, str):
self._fee = Decimal(value)
elif isinstance(value, Decimal):
self._fee = value

fee = property(get_fee, set_fee)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

# -*- coding: utf-8 -*-

fromdecimalimportDecimal

classFees(object)

«»»»»»

def__init__(self)

«»»Конструктор»»»

self._fee=None

defget_fee(self)

«»»

        Возвращаем текущую комиссию
        «»»

returnself._fee

defset_fee(self,value)

«»»

        Устанавливаем размер комиссии
        «»»

ifisinstance(value,str)

self._fee=Decimal(value)

elifisinstance(value,Decimal)

self._fee=value

fee=property(get_fee,set_fee)

Мы добавили одну строк в конце этого кода. Теперь мы можем делать что-то вроде этого:

Python

f = Fees()
f.set_fee(«1»)
print(f.fee) # Decimal(‘1’)

f.fee = «2»
print( f.get_fee() ) # Decimal(‘2’)

1
2
3
4
5
6

f=Fees()

f.set_fee(«1»)

print(f.fee)# Decimal(‘1’)

f.fee=»2″

print(f.get_fee())# Decimal(‘2’)

Как мы видим, когда мы используем свойство таким образом, это позволяет свойству fee настраивать и получать значение без поломки наследуемого кода. Давайте перепишем этот код с использованием декоратора property, и посмотрим, можем ли мы получить его для разрешения установки.

Python

# -*- coding: utf-8 -*-
from decimal import Decimal

class Fees(object):
«»»»»»
def __init__(self):
«»»Конструктор»»»
self._fee = None

@property
def fee(self):
«»»
Возвращаем текущую комиссию — геттер
«»»
return self._fee

@fee.setter
def fee(self, value):
«»»
Устанавливаем размер комиссии — сеттер
«»»
if isinstance(value, str):
self._fee = Decimal(value)
elif isinstance(value, Decimal):
self._fee = value

if __name__ == «__main__»:
f = Fees()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

# -*- coding: utf-8 -*-

fromdecimalimportDecimal

classFees(object)

«»»»»»

def__init__(self)

«»»Конструктор»»»

self._fee=None

@property

deffee(self)

«»»

        Возвращаем текущую комиссию — геттер
        «»»

returnself._fee

@fee.setter

deffee(self,value)

«»»

        Устанавливаем размер комиссии — сеттер
        «»»

ifisinstance(value,str)

self._fee=Decimal(value)

elifisinstance(value,Decimal)

self._fee=value

if__name__==»__main__»

f=Fees()

Данный код демонстрирует, как создать сеттер для свойства fee. Вы можете делать это, декорируя второй метод, который также называется fee с декоратором, под названием <@fee.setter>. Сеттер будет вызван, когда вы сделаете что-то вроде следующего:

Python

f = Fees()
f.fee = «1»

1
2

f=Fees()

f.fee=»1″

Если вы взгляните на подписи под свойством, то это будут fget, fset, fdel и doc в качестве аргументов. Вы можете создать другой декорируемый метод, используя то же название связи с функцией delet при помощи <@fee.deleter*>*, если вы хотите поймать команду **del для атрибута.

Python Set Operations

Sets can be used to carry out mathematical set operations like union, intersection, difference and symmetric difference. We can do this with operators or methods.

Let us consider the following two sets for the following operations.

Set Union

Set Union in Python

Union of A and B is a set of all elements from both sets.

Union is performed using operator. Same can be accomplished using the method.

Output

{1, 2, 3, 4, 5, 6, 7, 8}

Try the following examples on Python shell.

Set Intersection

Set Intersection in Python

Intersection of A and B is a set of elements that are common in both the sets.

Intersection is performed using operator. Same can be accomplished using the method.

Output

{4, 5}

Try the following examples on Python shell.

Set Difference

Set Difference in Python

Difference of the set B from set A(A — B) is a set of elements that are only in A but not in B. Similarly, B — A is a set of elements in B but not in A.

Difference is performed using operator. Same can be accomplished using the method.

Output

{1, 2, 3}

Try the following examples on Python shell.

Set Symmetric Difference

Set Symmetric Difference in Python

Symmetric Difference of A and B is a set of elements in A and B but not in both (excluding the intersection).

Symmetric difference is performed using operator. Same can be accomplished using the method .

Output

{1, 2, 3, 6, 7, 8}

Try the following examples on Python shell.

Инициализация Frozenset

В приведенном ниже примере мы инициализировали Frozenset с помощью метода Python , передав различные итерации, такие как список, кортеж, набор и словарь.

# Initialisation
list1 = 

fz = frozenset(list1)  # from list object
print(fz)

fz = frozenset()  # from list
print(fz)

fz = frozenset({5: 'a', 4: 'B', 3: 'C', 2: 'D', 1: 'E', 0: '0'})# from dict
print(fz)

fz = frozenset({'Python', 'Java', 'C', 'C++', 'Kotlin', 'R'})# from set
print(fz)

fz = frozenset((17, 55, 26, 90, 75, 34)) # from tuple
print(fz)

Выход:

frozenset({1, 2, 3, 4, 5})
frozenset({1, 2, 3, 4, 5})
frozenset({0, 1, 2, 3, 4, 5})
frozenset({'Java', 'Kotlin', 'Python', 'C', 'R', 'C++'})
frozenset({34, 90, 75, 17, 55, 26})

Для каждого случая мы получаем объект frozenset с соответствующими итеративными элементами в нем. Но учтите, что в случае словаря рассматриваются только ключи.

product(*iterables, repeat=1)

Пакет itertools содержит небольшую, но очень полезную функцию, которая создает продукты Cartesian из ряда вложенных итерируемых. Да, эта функция является продуктом. Давайте посмотрим на то, как это работает!

Python

from itertools import product

arrays =
cp = list(product(*arrays))

print(cp)

1
2
3
4
5
6

fromitertoolsimportproduct

arrays=(-1,1),(-3,3),(-5,5)

cp=list(product(*arrays))

print(cp)

Результат:

Python

1
2
3
4
5
6
7
8

Здесь мы импортируем product, затем устанавливаем список кортежей, который мы назначаем переменным массивам. Далее, мы вызываем product с этими массивами. Вы заметите, что мы вызываем их при помощи *arrays. Благодаря этому список будет «взорван» или применен к функции продукта в определенной последовательности. Это значит, что вы передаете 3 аргумента, вместо одного. Если хотите, попробуйте вызвать её, используя предварительно скопированную в массивы звездочку.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *