[0, 1][10:]
len(' '.join(list(map(str, [[0], [1]]))))
import sys arr_1 = [] arr_2 = arr_1 print(sys.getrefcount(arr_1))
class Variable: def __init__(self, name, value): self._name = name self._value = value @property def value(self): print(self._name, 'GET', self._value) return self._value @value.setter def value(self, value): print(self._name, 'SET', self._value) self._value = value var_1 = Variable('var_1', 'val_1') var_2 = Variable('var_2', 'val_2') var_1.value, var_2.value = var_2.value, var_1.value
def f_g(): yield 43 return 66 print(f_g())
arr = [[]] * 5 arr_1, arr_2 = arr, arr for k, arr in enumerate((arr_1, arr_2)): arr[0].append(k) arr = (arr_1, 5, arr_2) print(arr)
def f1(arr): l1 = sorted(arr) l2 = [i for i in l1 if i < .5] return [i * i for i in l2] def f2(arr): l1 = [i for i in arr if i < .5] l2 = sorted(l1) return [i * i for i in l2] def f3(arr): l1 = [i * i for i in arr] l2 = sorted(l1) return [i for i in l1 if i < (.5 * .5)]
if __debug__: assert False, ("error")
_MangledGlobal__mangled = "^_^" class MangledGlobal: def test(self): return __mangled assert MangledGlobal().test() == "^_^"
<html> <link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" /> <script defer src="https://pyscript.net/alpha/pyscript.js"></script> <body> <py-script> print(__name__) print(__file__) </py-script> </body> </html>
Высокоуровневый язык программирования общего назначения с динамической типизацией и автоматическим управлением памятью. Является полностью объектно-ориентированным, т.к. все элементы в нем объекты.
В феврале 1991 г. Гвидо ван Россум опубликовал Python v.0.9.0
Mutable (изменяемые)
- Set
- List
- Dictionary
Immutable (неизменяемые)
- String
- Integer
- Float
- Complex
- Boolean
- None
- Tuple
- Frozenset
Lambda function - анонимная однострочная функция. Без них вполне можно обойтись, но благодаря им код становится проще и лаконичнее.
PEP (Python Enhancement Proposal) - это документ составленный Гвидо ван Россумом, Барри Варшавой и Ником Когланом в 2001 году содержащий рекомендации по написанию кода на Python с целью улучшения читабельности и логичности кода.
print(dir(object)) for attr in dir(object): print(attr) print(attr.__doc__, '\n')
def my_function(a, b): '''Explanation of function called docstring''' return a ** b print(my_function.__doc__) # get function description
List - изменяемый (mutable) тип данных т.е. мы можем добавлять\удалять\модифицировать элементы внутри него.
Tuple - неизменяемый (immutable), операции удаления\добавления\модификации в нем недоступны. Исключение, если в Tuple содержится, например, List или Set, в таком случае мы можем модифицировать элементы внутри данных типов.
Сами значения индексов могут быть только положительными т.к. они автоинкрементируемые (увеличиваются на единицу от предыдущего).
Однако, мы можем обращаться к элементам списков по отрицательному индексу, это действие называет back indexing.
arr = [1, 2, 3] print(list(enumerate(arr))) # [(0, 1), (1, 2), (2, 3)] print([{'index': index, 'value': value} for index, value in enumerate(arr)]) # [{'index': 0, 'value': 1}, {'index': 1, 'value': 2}, {'index': 2, 'value': 3}] print(arr[0]) # 1 print(arr[-1]) # 3 print(arr[-2]) # 2
Проигнорировать участок кода, а именно scope в котором он указан.
Многопоточные приложения (модуль threading).
Мультипроцессорные приложения (модуль multiprocessing).
- Потоки существуют внутри процесса
- В одном процессе может быть несколько потоков
- Потоки в одном процессе разделяют состояние и память родительского процесса
Критерий | Процессы (Process) | Потоки (Thread) |
---|---|---|
Распределение памяти | Память не распределяется между процессами | Память распределяется между потоками внутри процесса |
Используемый объем памяти | Много | Мало |
Привязка к процессору и устройствам ввода\вывода | Оптимизирован для задач, связанных с ЦП | Оптимизирован для задач, связанных с вводом-выводом |
Время запуска | Медленнее потока | Быстрее процесса |
Прерываемость | Дочерние процессы прерываемы | Потоки не прерываются |
print([method_name for method_name in dir(YOUR_OBJECT) if callable(getattr(YOUR_OBJECT, method_name))])
*args - неограниченное кол-во необязательных аргументов рассматриваемое в теле функции в виде Tuple.
**kwargs - неограниченное кол-во необязательных именованных аргументов рассматриваемое в теле функции в виде Dictionary.
Наследование, инкапсуляция и полиморфизм являются основными столпами ООП, Python реализует их в полной мере, соответственно, полностью поддерживает ООП.
globals() - всегда возвращает словарь пространства имен модуля
locals() - всегда возвращает в словарь в текущем пространстве имен
vars() - возвращается либо в словаре текущего пространства имен (если вызывается без аргументов), или в словаре аргумента.
def my_function(a, b): result = a / b print(globals()) print(locals()) # {'a': 5, 'b': 10, 'result': 0.5} print(vars()) # {'a': 5, 'b': 10, 'result': 0.5} return result class MyClass: a = 1 b = 2 print(globals()) print(locals()) print(vars()) print(vars(MyClass)) my_function(5, 10)
__dict__ содержит в себе все атрибуты объекта.
class MyClass: a = 1 b = 2 print(MyClass.__dict__) # {'__module__': '__main__', 'a': 1, 'b': 2, '__dict__':, '__weakref__': , '__doc__': None}
touch app.py
# print('hello') print('hello
python -m py_compile app.py
Параметр self является ссылкой на текущий экземпляр класса и используется для доступа к переменным, принадлежащим классу.
Декоратор - это функция которая неявным образом модифицирует поведение другой (декорируемой функции)
import time def execution_time(function): def wrapper(): start = time.time() function() print(f'{function.__name__} execution time: {time.time() - start} seconds') return wrapper @execution_time def my_func1(): time.sleep(2.5) @execution_time def my_func2(): time.sleep(1.5) my_func1() my_func2()
Ключами в словарях могут быть только объекты, поддерживающие хэширование (строки, числа). Использовать float не рекомендуется т.к. они хранятся в памяти ввиде приближений.
Использовать в качестве ключей списки, словари и другие изменяемые (мутабельные) типы не получится.
Основное отличие в структуре.
Модуль - это просто файл с кодом, который можно импортировать.
Пакет - это папка с несколькими модулями и файлом __init__.py
print('{0:08b}'.format(50)) # целое число в двоичную строку print(f'{50:08b}') # целое число в двоичную строку a = '00110010' print(int(a, 2)) # двоичная строка в число
Это т.н. конструктор. Отвечает за инициализацию экземпляров класса после их создания.
Slice (срез) вернет объект, представляющий часть итерируемого объекта, которая будет соответствовать шаблону, указанному в аргументах среза.
t2 = (4, 5, 6, 7, 8, 9, 1, 2, 3, 8, 15) print(set(t1).issubset(set(t2))) # True
def cart(product, items_in_cart=[]): items_in_cart.append(product) return items_in_cart product1 = cart('Product 1') print(product1) # ['Product 1'] product2 = cart('Product 2') print(product2) # ['Product 1', 'Product 2'] # верул два товара, хотя мы не добавляли 2-й, в это и заключается проблема
Причина такого поведения в том, что когда интерпретатор определяет функцию, он также создает аргумент по умолчанию. Затем он связывает этот аргумент и созданный объект (ставит ссылку на него в памяти). В примере, Python выделил пустой список и привязал его к аргументу items_in_cart.
Пустой список создается один раз и аргумент items_in_cart указывает на него в течение всего времени существования функции. Всякий раз, когда вызывается функция снова, без указания items_in_cart, то она будет использовать значение по умолчанию, которое было создано при определении функции.
!!! Чтобы избежать подобного поведения, аргументы по умолчанию должны быть неизменяемыми:
def cart(product, items_in_cart=None): if items_in_cart is None: items_in_cart = [] items_in_cart.append(product) return items_in_cart product1 = cart('Product 1') print(product1) # ['Product 1'] product2 = cart('Product 2') print(product2) # ['Product 2']
@staticmethod - декоратор который объявляет метод внутри класса статическим. Грубо-говоря просто функция внутри класса, которая ничего не знает о классе или экземпляре класса.
@classmethod - в целом похож на @staticmethod с той разницей, что первым аргументом мы передаем cls со всеми его методами и атрибутами класса, но не его экземпляра.
@property - не вдаваясь в детали, декоратор property позволяет задать getter, setter, deleter, а с помощью аргумента doc можно задать docstring для метода.
Выполняет процесс до тех пор пока он полностью не завершится, при этом блокирует выполнение нижестоящего кода.
Асинхронный код не блокирует выполнение нижестоящего кода а создает т.н. сoroutines (сопрограммы) которые выполняются с прерыванием, при этом сохраняют состояние на котором произошло прерывание.
import asyncio import random async def task1(): print('Run task 1') await asyncio.sleep(random.randint(1, 3)) print('Complete task 1') async def task2(): print('Run task 2') await asyncio.sleep(random.randint(1, 3)) print('Complete task 2') async def task3(): print('Run task 3') await asyncio.sleep(random.randint(1, 3)) print('Complete task 3') async def main(): t1 = asyncio.create_task(task1()) t2 = asyncio.create_task(task2()) t3 = asyncio.create_task(task3()) await asyncio.gather(t1, t2, t3) asyncio.run(main())
print(-30 % 10) # 0
Возвращает уникальный идентификатор объекта который присваивается ему интерпретатором при создании.
Итератор - это объект реализующий методы __iter__() и __next__() который можно "перебрать" (проитерировать) только один раз.
Генератор - это объект, который сразу при создании не вычисляет значения всех своих элементов а хранит в памяти только последний элемент.
Каждый генератор является итератором, но не наоборот.
Фактически генератор это более элегантный способ написания итераторов.
Для создания функций-генераторов.
Метод __iter__ возвращает объект итератора.
Метод __next__ возвращает следующий элемент из последовательности.
Оператор with использует магические методы __enter__ и __exit__, позволяет сохранять\восстанавливать глобальные состояния, блокировать\разблокировать различные ресурсы, открывать\закрывать файлы, подключаться\закрывать соединения с сервером, базой данных и т.п. в автоматическом режиме.
Используя with, мы задаем некий контекст действий выйдя за рамки которого действие завершается.
Можно воспользоваться PyInstaller.
import copy arr = [1, 2, 3] new_arr = arr.copy() # просто копия print(arr is new_arr) # False new_arr_deep = copy.deepcopy(arr) # если объект поддерживает глубокое копирование print(arr is new_arr_deep) # False
Python управляет памятью автоматически. Для этих целей в языке присутствует garbage collector (сборщик мусора).
Сборщик мусора запускается периодически и подсчитывает ссылки на на объекты (reference counting), увеличивает или уменьшает их количество.
В случае если объекты ссылаются друг на друга и больше не используются сборщик мусора использует т.н. "обнаружение циклов" (cycle detector) и освобождает память, если они не используются
Кроме того, существует механизм generational garbage collection, которые разбивает объекты на "поколения". Также существует модуль gc, благодаря которому можно осуществить принудительную сборку мусора gc.collect()
.
var1 = 100 def my_function(): global var1 var1 = 500 my_function() print(var1) # 500
Если все делать внимательно, аккуратно и уместно, то ничего криминального в использовании глобальных переменных нет. Однако их использование считается плохой практикой и использовать их без крайней необходимости не рекомендуется т.к. это может привести к неожиданным последствиям, вызвать ошибки и доставить массу неприятностей. Поэтому лучше стараться избегать использования глобальных переменных если это возможно.
__slots__ используется для оптимизации памяти и ускорения работы с объектами созданными на основе класса.
Python создает для каждого экземпляра класса словарь __dict__ который содержит все его атрибуты, в случае если в классе много атрибутов и вы создаете много объектов на основе класса это может привести к большому расходу памяти. __slots__ позволяет определить какие именно атрибуты класса должны быть созданы в объекте и записывает их не в словарь а в список, что позволяет ускорить работу и уменьшить расход памяти.
class User1: def __init__(self, name, email): self.name = name self.email = email class User2: __slots__ = ['name', 'email'] def __init__(self, name, email): self.name = name self.email = email u1 = User1('user1', 'user1@gmail.com') print(u1.__dict__) # {'name': 'user1', 'email': 'user1@gmail.com'} u2 = User2('user2', 'user2@gmail.com') # print(u2.__dict__) # AttributeError: 'User2' object has no attribute '__dict__'. print(u2.__slots__) # ['name', 'email']
Пространство имен - это система которая гарантирует что все имена функций, переменных и объектов будут уникальны и могут использоваться без каких-либо конфликтов.
Некоторые пространства имен (на самом деле их больше): main, name, builtins, globals, locals...
Осуществляется автоматически с помощью сборщика мусора (garbage collector).
Метаклассы довольно сложная концепция для понимания на первый взгляд относящаяся к метапрограммированию.
Википедия дает следующее определение метаклассам:
Метакласс - в объектно-ориентированном программировании это класс, экземпляры которого в свою очередь являются классами.
Более простыми словами, метаклассы - это классы которые определяют свойства и поведение других классов, они используются для изменения способа которым Python создает и обрабатывает классы.
Ниже простой пример как это работает:
class MyMetaClass(type): def __new__(cls, name, bases, dct): dct['some_attribute'] = 'this attribure added from Metaclass' return super(MyMetaClass, cls).__new__(cls, name, bases, dct) class A(metaclass=MyMetaClass): pass class B(A): pass class C(B): pass print(A.some_attribute) # this attribute added from Metaclass print(B.some_attribute) # this attribute added from Metaclass print(C.some_attribute) # this attribute added from Metaclass
Стоит ли использовать метаклассы?
- Если вы знаете что делаете и нет более простых путей решения задачи, то да, стоит. В подавляющем большинстве случаев можно обойтись без них.
Метаклассы могут быть полезны если вы захотите разработать собственный фреймворк или создать класс на основе заранее неизвестных данных, когда он будет формироваться в хоте выполнения различных условий.
pdb - это библиотека для отладки кода.
print([0, 1][10:]) # []
Это срез, который выбирает все элементы, начиная с 10-го индекса, а в списке всего два элемента, соответственно, вернется пустой список.
NewClass = type('NewClass', (), {'attr': 100, 'method': lambda self: self.attr * 2}) obj1 = NewClass() print(obj1.attr) # 100 print(obj1.method()) # 200
Используйте модуль reload().
from importlib import reload import my_module my_module.do_something() reload(my_module)
import functools def try_run_function(tries): def func_wrp(function): @functools.wraps(function) def wrp(*args, **kwargs): for i in range(1, tries+1): try: run_function = function(*args, **kwargs) return run_function except Exception as e: print(f'Try #{i}', 'Error:', e) raise Exception(f'{function.__name__} failed after {tries} runs') return wrp return func_wrp @try_run_function(5) def my_function(x, y): return x ** y print(my_function(2, 3)) # ok print(my_function('2', 3)) # rise an exception
print(list(map(str, [[0], [1]]))) # ['[0]', '[1]'] список с двумя строками res = ' '.join(list(map(str, [[0], [1]]))) # print(res, type(res)) # строка '[0] [1]'print(len(res)) # 7 - количество символов включая пробел в строке print(len(' '.join(list(map(str, [[0], [1]]))))) # 7
В плане синтаксиса - да, Python воспринимается на порядок легче других языков. В остальном нет, это полноценный и развитый язык который требует серьёзного изучения.
- Garbage collector не всегда работает эффективно что может привести к утечками памяти
- Документация к некоторым библиотекам и модулям оставляет желать лучшего
- Динамическая типизация - однозначное зло
- ООП слишком упрощен, мне больше нравится реализация в PHP и Java
- Python уступает по скорости работы языкам C-группы, в задачах машинному обучению и научных вычислениях
Большинство проблем "высосаны из пальца", в любых языках присутствуют проблемы, нет ничего идеального.
Если код в блоке try успешно выполнится.
try: # try to execute something except: # in case of error else: # in case of success try block finally: # executes regardless success or fail
Да, поддерживает.
class A: def method1(self): return 'method1' class B: def method2(self): return 'method2' class C(A, B): pass obj = C() print(obj.method1(), obj.method2()) # method1 method2
Словари и множества реализованы в виде хэш-таблицы.
Хеш-таблица - это структура данных, в которой все элементы хранятся в виде пары ключ-значение, где:
ключ - уникальное число, которое используется для индексации значений, генерируется с помощью функции хеширования;
значение - данные, которые связаны с этим ключом.
O(1) - константная временная сложность (операции выполняются максимально быстро)
O(n) - линейная временная сложность (средняя скорость выполнения)
Сложность получения элемента в Set:
Проверить наличие элемента в множестве: O(1)
Отличие множества A от B: O (длина A)
Пересечение множеств A и B: O (минимальная длина A или B)
Объединение множеств A и B: O(N) , где N это длина (A) + длина (B).
Сложность получения элемента в Dictionary:
Получение элемента: O(1)
Установка элемента: O(1)
Удаление элемента: O(1)
Проход по словарю: O(n)
Потребляемый объем памяти:
import sys d1 = dict() s1 = set() print(sys.getsizeof(d1)) # 64 bytes print(sys.getsizeof(s1)) # 216 bytes
MRO (Method Resolution Order) - определяет порядок наследования атрибутов и методов классов. MRO используется при множественном наследовании.
class A: def method1(self): print('A method1') class B: def method1(self): print('B method1') class C(B): def method1(self): print('C method1') class D(C): pass class E(A, D): pass d = D() d.method1() # C method1 e = E() e.method1() # A method1
По ссылке на объект.
def rename(name): name = 'NewName' return name name = 'Vasya' print(rename(name)) # NewName print(name) # Vasya
В случае с мутабельными типами данных, изменяемый объект будет модифицирован
def add_to_list(arr): arr.append('Vasya') my_list = [1, 2, 3] add_to_list(my_list) print(my_list) # [1, 2, 3, 'Vasya']
Статический анализ кода - это анализ качества кода без его запуска.
Динамический анализ кода - это анализ качества кода с помощью его запуска\компиляции в реальном или виртуальном процессах.
Инструментов для статического анализа очень много, перечеслю основные:
import sys arr_1 = [] arr_2 = arr_1 print(sys.getrefcount(arr_1)) # 3
sys.getrefcount возвращает счетчик количества ссылок на объект. Счетчик обычно больше на единицу чем фактических ссылок т.к. он считает за ссылку переданный в нее аргумент.
Global Interpreter Lock (GIL) - это механизм блокировки потоков который гарантирует что в любой момент времени в состоянии выполнения может находиться только один поток.
Если вы разрабатываете однопоточные программы, возможно вам этот принцип не знаком.
GIL существует т.к. является важной частью интерпретатора Python.
Python интерпретируемый язык, он не требует компиляции, интерпретатор выполняет код напрямую.
При первом запуске интерпретатор компилирует содержимое .py в байт-код находящийся в __pycache__ с расширением .pyc, далее байт-код выполнятся через PVM (Python Virtual Machine).
Если я правильно понял вопрос, то речь идет о репликации (копировании) кода. Это можно сделать как угодно, от создания архива отправки по почте )), до использования Git, Docker, pyinstaller и т.д.
Дескрипторы - это объекты Python, которые реализуют метод протокола дескрипторов, что дает вам возможность определять поведение объекта когда мы обращаемся к нему через его методы или атрибуты.
class AttributeDescriptor(): def __get__(self, obj, type=None) -> object: print('получили значение атрибута') return 'something...' def __set__(self, obj, value) -> None: print("пропробовали задать значение атрибута") raise AttributeError("Не вариант") class MyClass(): attr1 = AttributeDescriptor() my_object = MyClass() a = my_object.attr1 print(a) # my_object.attr1 = 56 # AttributeError: Не вариант
Декораторы - это функция которая модифицирует другую функцию неявным образом.
Разница между дескрипторами и декораторами в том, что дескрипторы определяют поведение атрибутов объекта, а декораторы изменяют поведение функций. Хотя декораторы могут быть использованы для реализации дескрипторов:
class MyClass(): @property def attr1(self) -> object: print('получили значение атрибута') return 'something...' @attr1.setter def attr1(self, value) -> None: print("попробовали задать значение атрибута") raise AttributeError("Не вариант") my_object = MyClass() a = my_object.attr1 print(a) # my_object.attr1 = 56 # AttributeError: Не вариант
Из-за garbage collector т.к. на момент завершения работы кода не все объекты попадают в сборщик мусора.
class Variable: def __init__(self, name, value): self._name = name self._value = value @property def value(self): print(self._name, 'GET', self._value) return self._value @value.setter def value(self, value): print(self._name, 'SET', self._value) self._value = value var_1 = Variable('var_1', 'val_1') # вызывается getter value var_2 = Variable('var_2', 'val_2') var_1.value, var_2.value = var_2.value, var_1.value # вызывается setter value ''' Outputs: var_2 GET val_2 var_1 GET val_1 var_1 SET val_1 var_2 SET val_2 '''
String Interning - это оптимизация строковых данных, когда Python с целью экономии ресурсов записывает одинаковые строки в одну область памяти.
a = 'Vasya' b = 'Vasya' print(a is b) # True т.к. сохранены в одну область памяти c = 'Vasya learning Python' d = 'Vasya learning Python' print(c is d) # True в ранних релизах Python 3.x было False т.к. интернирование срабатывало на строках до 5-ти символов, сейчас значение увеличено e = 'Vasya learning Python and JavaScript' f = 'Vasya learning Python' f += ' and JavaScript' print(e is f) # False т.к. хоть строка в области 'f' равна по значению 'e', но она составлена из разных областей и интернирование не работает
Слово "бинарные" сбивает с толка. Проще-говоря как сохранить в пакете и запустить все библиотеки необходимые для работы проекта.
С помощью pip.
pip install flask flask-bcrypt flask-wtf email-validator touch requirements.txt pip freeze > requirements.txt
Для установки
pip install -r requirements.txt
Как вариант можно рассмотреть Wheel.
Из-за того что хвостовая рекурсия может привести к переполнению стека вызовов она не оптимизируется автоматически.
Относительно Tail recursion можно почитать в статье Guido van Rossum Tail Recursion Elimination и в Final Words on Tail Calls.
# Tail recursion def countdown_tr(n): if n < 0: return print('Counting down', n) countdown_tr(n - 1) countdown_tr(4) # Replace tail recursion with while loop def countdown_loop(n): while True: if n < 0: return n print('Counting down', n) n -= 1 countdown_loop(3)
Wheels и Eggs - это форматы дистрибуции пакетов для pip.
Egg появился в 2004 г., Wheel в 2012 г.. Egg может содержать .pyc файлы, что является проблемой при инсталляции пакета на разных платформах. Wheel является стандартом на текущий момент и лишен всех недостатков Egg.
Честно-говоря странная идея. Если погуглить, то оказывается что с Python возможно всё Embedding Python in Your C Programs или библиотеку C в Python:
from ctypes import cdll libm_so = cdll.LoadLibrary('libm.so') print(libm_so.sqrt(7.0)) # квадратный корень от 7.0
Универсальных рецептов нет, надо смотреть на сам код который требует улучшения. Первое что приходит на ум - произвести рефакторинг, использовать более быстрые алгоритмы, попробовать найти альтернативу вашим функциям среди встроенным в язык, попробовать обрабатывать меньше данных за единицу времени.
Попробовать Cython, PyPy, Threads, Multiprocessing, AsyncIO, NumPy... вариантов много.
Вернусь к тому с чего начал: xтобы ускорить код, надо смотреть не сколько на сам старый код, а на задачу.
При запуски кода Python интерпретатор компилирует ваш код в байт-код и сохраняет в папке __pycache__, внутри этой папке появляются скомпилированные с помощью байт-кода файлы с расширением .pyc
в которых содержится оптимизированная версия вашего кода.
Смысл всего этого процесса в том, что ваша программа будет запускаться немного быстрее. Однако вы должны понять, что не следует передавать папку __pycache__ с её содержимым другим пользователям, она должна быть включена в .gitignore
.
Суперполезная концепция в Python позволяет создавать инкапсулированные (изолированные) среды для разработки различных проектов.
Предположим, вы работаете над несколькими проектами и каждый из них требует различного набора модулей\библиотек\фреймворков, устанавливать все их глобально через pip плохая идея т.к. это захламит ваш проект. Отличное решение Virtualenv или pyenv.
Создали изолированную виртуальную среду, активировали её, установили все пакеты и радуемся жизни.
virtualenv .venv source .venv/bin/activate # или .venv\Scripts\activate (.venv): pip install -r requirements.txt (.venv): python manage.py runserver
Python объектно-ориентированный язык, а ООП - это ничто иное как императивная парадигма. Соответственно Python является преимущественно императивным языком, хотя поддерживает и декларативную парадигму.
Справедливо назвать его мультипарадигменным языком.
Package Manager - это инструмент который отвечает за установку\удаление\обновление модулей\библиотек\фреймворков и их зависимостей.
pip - основной менеджер пакетов в Python, другие: easy_install, conda и т.д.
NumPy более эффективно использует память для работы с большими объемами данных, а также использует более быстрые алгоритмы. Проще-говоря NumPy работает быстрее.
Речь идет о замыкании (closure):
def multiplier(x): def mult(y): return x * y return mult times5 = multiplier(5) times10 = multiplier(10) print(times5(10)) print(times5(5)) print(times10(10)) print(times10(5))
Подробнее о замыканих можно узнать Closures - How to Use Them and Why They Are Useful.
def f_g(): yield 43 return 66 print(f_g()) # <generator object f_g at 0x00000283E10D8930>
В результате получим объект генератор т.к. функция является генератором из-за присутствия слова yield. Если хотите получить результат функции, то:
print(next(f_g())) # 43 # или for item in f_g(): print(item) # 43 # return генераторы игнорируют
Словари Python реализованы в виде хеш-таблиц.
class CustomDictionary: def __init__(self): self.table_size = 10 self.keys = [None] * self.table_size self.values = [None] * self.table_size def __setitem__(self, key, value): get_index = hash(key) % self.table_size self.keys[get_index] = key self.values[get_index] = value def __getitem__(self, key): get_index = hash(key) % self.table_size return self.values[get_index] a = CustomDictionary() a['name'] = 'Vasya' a['age'] = 34 print(a['name']) # Vasya print(a['age']) # 34
touch text.txt Lorem ipsum dolor sit amet consectetur adipisicing elit. Ipsam debitis incidunt deleniti ab accusantium ullam! Dignissimos cum nesciunt quae incidunt earum magni odit optio sunt velit est quasi, neque eligendi.
print(sum(1 for row in open('text.txt') for c in row if c.isupper()))
.pth
- это файл в котором указываются пути к модулям которые вы хотите импортировать в ваше пространство имен.
Используются в случае если необходимые модули лежат где-то вне файлов проекта, например, на другом разделе жесткого диска.
Как воспользоваться данной фичей?
Для начала посмотрите где Python ищет модули:
import sys print(sys.path) # ['c:\\Users\\Denis\\Desktop\\sb', 'C:\\Python311\\python311.zip', 'C:\\Python311\\DLLs', 'C:\\Python311\\Lib', 'C:\\Python311', 'C:\\Python311\\Lib\\site-packages']
Далее создайте файл с любым именем, но с раcширением .pth
в папке C:\Python311\Lib\site-packages
или site-packages
вашей virtualenv.
У меня получилось так:
touch new_path.pth # file content: D:\new_folder
Теперь еще раз запустим sys.path
и увидим что D:\new_folder
в списке:
print(sys.path) # ['c:\\Users\\Denis\\Desktop\\sb', 'C:\\Python311\\python311.zip', 'C:\\Python311\\DLLs', 'C:\\Python311\\Lib', 'C:\\Python311', 'C:\\Python311\\Lib\\site-packages', 'D:\\new_folder']
Мой вам совет: не морочьте себе и другим людям голову с .pth
файлами, могут быть проблемы.
На этот вопрос так и хочется ответить: те которые нужны, но видимо хотят проверить знакомы ли вы с этими модулями впринципе.
Модуль Collections предоставляет различные типы контейнеров
Хорошая статья про Collections и несколько примеров:
from collections import Counter, ChainMap # Counter может посчитать кол-во элементов в итерируемом объекте arr = [1, 1, 2, 3, 1, 2, 3, 4, 5, 4, 6] print(Counter(arr)) # Counter({1: 3, 2: 2, 3: 2, 4: 2, 5: 1, 6: 1}) arr2 = ['F', 'L', 'L', 'X', 'W', 'X', 'A', 'L'] print(Counter(arr2)) # Counter({'L': 3, 'X': 2, 'F': 1, 'W': 1, 'A': 1}) # ChainMap позволяет интересно и удобно работать со словарями d1 = {'a': 1, 'b': 2} d2 = {'c': 3, 'd': 4} d3 = {'e': 5, 'f': 6} chain = ChainMap(d1, d2, d3) print(chain.keys()) # KeysView(ChainMap({'a': 1, 'b': 2}, {'c': 3, 'd': 4}, {'e': 5, 'f': 6})) print(chain.values()) # ValuesView(ChainMap({'a': 1, 'b': 2}, {'c': 3, 'd': 4}, {'e': 5, 'f': 6})) # вот в этом их преимущество: у нас три разных словаря, объединив их с помощью ChainMap, работаем с ними как с одним print(chain['a'], chain['c'], chain['f']) # 1 3 6
Модуль Itertools предоставляет методы для создания сложных итераторов и эффективной работы с ними.
Хорошая статья про Itertools и несколько примеров:
from itertools import count, repeat, chain, accumulate # count iter_even =(count(start=0, step=2)) print("Список четных:", list(next(iter_even) for _ in range(6))) # [0, 2, 4, 6, 8, 10] список из 6-ти четных чисел iter_odd = (count(start=1, step=2)) print("Список нечетных:", list(next(iter_odd) for _ in range(6))) # [1, 3, 5, 7, 9, 11] список из 6-ти нечетных чисел # repeat print(list(repeat(100, 5))) # [100, 100, 100, 100, 100] создаст итератор из 5-ти повторяющихся чисел # chain arr1 = [1, 2, 3] arr2 = [4, 5, 6] print(list(chain(arr1, arr2))) # [1, 2, 3, 4, 5, 6] объединит списки в один # accumulate arr3 = [1, 2, 3, 4, 5, 6] res = accumulate(arr3, lambda n1, n2: n1 * n2) print([n for n in res]) # [1, 2, 6, 24, 120, 720] умножит предыдущее число на следующее
PYTHONOPTIMIZE удаляет из кода операторы assert, игнорирует любой код находящийся внутри блока __debug__, удаляет docstrings.
''' Try to run file app.py with: touch app.py python app.py python -O app.py python -OO app.py ''' def my_function(): '''my_function description''' return 'my_function' print('prod') print(my_function.__doc__) assert 1 == 1 # ok # this will be ignored in -O and -OO if __debug__: print('debug') print(my_function.__doc__) assert 1 == 2 assert 1 == 2
arr = [[]] * 5 arr_1, arr_2 = arr, arr for k, arr in enumerate((arr_1, arr_2)): arr[0].append(k) arr = (arr_1, 5, arr_2) print(arr) # ([[0, 1], [0, 1], [0, 1], [0, 1], [0, 1]], 5, [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1]])
Теперь рассмотрим как это работает подробнее:
arr = [[]] * 5 print(arr) # [[], [], [], [], []] получаем пять пустых вложенных списков arr_1, arr_2 = arr, arr print(arr_1, arr_2) # [[], [], [], [], []] [[], [], [], [], []] делаем их алиасы в двух переменных print(list(enumerate((arr_1, arr_2)))) # [(0, [[], [], [], [], []]), (1, [[], [], [], [], []])] конвертируем их в кортежи со счетчиком for k, arr in enumerate((arr_1, arr_2)): print(arr) # [[0], [0], [0], [0], [0]] arr[0].append(k) # добавляем во вложенные списки порядковый номер 1 из arr_2 в enumerate print(arr) # [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1]] добавляем к каждому из вложенных списков ключ arr = (arr_1, 5, arr_2) # создаем кортеж в котором первый элемент это arr_1, второй 5, а третий alias arr - arr_2 print(arr) # итог ([[0, 1], [0, 1], [0, 1], [0, 1], [0, 1]], 5, [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1]])
Полный список environment variables в Python. Эти переменные среды влияют на поведение Python.
Пример:
Вы хотите собрать проект в Docker-контейнер и не хотите что бы внутри него создавались __pycache__ и .pyc, это легко можно сделать добавив в Dockerfile настройки:
FROM python:3.11-slim ENV PYTHONDONTWRITEBYTECODE 1 ENV PYTHONUNBUFFERED 1
Опять-таки, применений им масса, поэтому адресую к началу ответа со ссылкой на список переменных среды.
Cython является надмножеством языка Python, который дополнительно поддерживает вызов функций C и объявление типов C для переменных и атрибутов класса. Cython позволяет генерировать код C из кода Python.
IronPython аналогично Cython, только для .NET
PyPy - это замена CPython. Он построен с использованием языка RPython. Основная причина использовать его вместо CPython - скорость.
Для чего они существуют:
- Скорость, чаще всего они работают быстрее
- Удобство работы как для Python-разработчиков, так и для .NET, C и в обратном порядке
gen = [n * 2 for n in list(range(6))] print([item for item in reversed(list(gen))]) # [10, 8, 6, 4, 2, 0]
Довольно простой пример выбора нечетных чисел с помощью filter:
print(list(filter(lambda n: n % 2 != 0, range(1, 11)))) # [1, 3, 5, 7, 9]
Просуммируем все числа в списке:
from functools import reduce print(list(range(6))) # [0, 1, 2, 3, 4, 5] print(reduce(lambda a, b: a + b, range(6))) # 15 print(sum(range(6))) # 15 (аналог для проверки)
print(_) # NameError: name '_' is not defined
_ - является переменной, она просто не определена.
Попробую объяснить совсем просто.
Library - это просто набор функций\методов. Вызывайте любой необходимый где вам надо и все будет работать.
Framework - это тоже набор функций и классов НО в нем есть некоторые требования к архитектуре (расположения и именования файлов и папок). Фреймворки создаются для решения каких-то задач. Например: разработка backend, frontend, сбор данных и т.д.
from time import time list1 = list(range(1000000)) start = time() def f1(arr): l1 = sorted(arr) # сортирует весь список, затратная операция l2 = [i for i in l1 if i < .5] return [i * i for i in l2] f1(list1) print(f'Execution time: {time() - start} sec') # ~ 0.06 sec start = time() def f2(arr): # самая быстрая из предложенных l1 = [i for i in arr if i < .5] l2 = sorted(l1) # не сортирет весь список return [i * i for i in l2] f2(list1) print(f'Execution time: {time() - start} sec') # ~ 0.05 sec start = time() def f3(arr): l1 = [i * i for i in arr] l2 = sorted(l1) # непонятно зачем это здесь return [i for i in l1 if i < (.5 * .5)] f3(list1) print(f'Execution time: {time() - start} sec') # # ~ 0.17 sec
Установил бы Memory Profiler и objgraph и протестировал код с их помощью.
NotImplementedError возникает когда в классе-наследнике не был реализован метод обозначенный в родительском классе.
from abc import ABC, abstractmethod class ParentAbstractClass(ABC): @abstractmethod def this_methon_sould_be_implemented(self): raise NotImplementedError('this_methon_sould_be_implemented() is not implemented') class ChildOne(ParentAbstractClass): def this_methon_sould_be_implemented(self): return 'OK' class ChildTwo(ParentAbstractClass): def this_methon_sould_be_implemented(self): super().this_methon_sould_be_implemented() obj1 = ChildOne() print(obj1.this_methon_sould_be_implemented()) obj2 = ChildTwo() print(obj2.this_methon_sould_be_implemented()) # NotImplementedError: this_methon_sould_be_implemented() is not implemented
if __debug__: assert False, ("error") # AssertionError: error
По-умолчанию __debug__ = True поэтому assert останавливает выполнение программы с ошибкой и комментарием "error". Это может быть нужно что бы вынудить пользователя запускать скрипт в режиме Optimized mode (с флагом -O), но об этом стоило бы сообщить в AssertionError.
if __debug__: assert False, ("please run this code in Optimized mode:\npython -O app.py")
Dunder (double underscore) или магические методы, это специальные методы с двойными подчеркиваниями в начале и в конце названия метода. Они определяют поведение объектов на основе класса в различных контекстах, например:
class MyClass: def __init__(self, name): # так будет весть себя при инициализации self.name = name # будет требовать указать атрибут 'name' def __str__(self): return 'Так объект класса будет вести себя в качестве строки' def __add__(self, other): return f'Такое поведение наблюдается при использовании + {other}' def __len__(self): # Такое поведение будет если применим к объекту функцию len() return 228 def __getitem__(self, *args): # Так объект будет вести себя если обратимся к нему как к элементу списка return ['Vasya', 228, *args] obj = MyClass('Vasya') print(obj) # Так объект класса будет вести себя в качестве строки print(obj + 1) # Такое поведение наблюдается при использовании + 1 print(len(obj)) # 228 print(obj[0], obj[1]) # ['Vasya', 228, 0] ['Vasya', 228, 1]
_MangledGlobal__mangled = "^_^" class MangledGlobal: def test(self): return __mangled assert MangledGlobal().test() == "^_^" print(MangledGlobal().test()) # '^_^'
Это явление называется Name mangling (коверканье имен). Обратите внимание на имя класса и переменной, думаю станет понятно в чем дело. Вот хорошая статья на тему Name mangling in Python.
Monkey patching - это динамическая замена атрибутов во время выполнения.
Предположим у нас есть некий класс с неким методом который получает данные со стороннего сервера\API в случае если нам потребуется написать тест данного метода то желательно избежать ситуации попадания в зависимость от стороннего источника данных, поэтому нам будет удобно заменить этот метод "заглушкой", которая будет возвращать какие-то заранее подготовленные данные, эти действия и называются "monkey patching".
class MyClass: def method(self): print('MyClass.method() is called') def monkey_function(self): print('monkey_function() is called') obj = MyClass() obj.method() # MyClass.method() is called MyClass.method = monkey_function # replace method() with monkey_function() obj = MyClass() obj.method() # monkey_function() is called
Здесь подвох в словосочетании "транзитивные зависимости", а на самом деле все просто - это модули и библиотеки которые ставятся вместе с необходимым вам пакетом\фреймворком.
Например: вы хотите установить Flask и пишете в терминалеpip install flask
и в результате получаете такую картину:
blinker click colorama Flask itsdangerous Jinja2 MarkupSafe pip setuptools Werkzeug wheel
Это и есть транзитивные зависимости т.е. это те пакеты и модули которые подтягиваются автоматически для работы какого-нибудь фреймворка, например.
Отсюда и ответ на вопрос как с ними работать, да очень просто с помощью pip
.
<html> <link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" /> <script defer src="https://pyscript.net/alpha/pyscript.js"></script> <body> <py-script> print(__name__) print(__file__) </py-script> </body> </html>
__main__ JsException(PythonError: Traceback (most recent call last): File "/lib/python3.10/site-packages/_pyodide/_base.py", line 429, in eval_code .run(globals, locals) File "/lib/python3.10/site-packages/_pyodide/_base.py", line 300, in run coroutine = eval(self.code, globals, locals) File "", line 1, in NameError: name '__file__' is not defined )
Можно попробовать что-то типа:
print(1 + 1) # 2 print('Hello, world!') # Hello, world!
PyScript позволяет запускать Python в HTML.
What's New In Python 3.10 отслеживать изменения по версия можно здесь.
Не сказал бы что Python работает под Windows медленно.