Тарифы Услуги Сим-карты

Язык программирования Python — подробно для начинающих. Перестаньте писать классы

Одна интересная вещь, которую я видел сегодня, это то, что когда я назначаю функцию члену класса, она становится несвязанным методом. Такие как:

Class Test(object): @classmethod def initialize_class(cls): def print_string(self, str): print(str) # Here if I do print(print_string), I see a function cls.print_proc = print_string # Here if I do print(cls.print_proc), I see an unbound method; so if I # get a Test object o, I can call o.print_proc("Hello")

2018-12-04T00:00Z

довольно сложно понять

Ну, это довольно сложная тема, и она связана с дескрипторами.

Давайте начнем с функции. Здесь все ясно - вы просто называете это, все предоставленные аргументы передаются при его выполнении:

>>> f = A.__dict__["f1"] >>> f(1) 1

Регулярный TypeError возникает в случае возникновения проблем с количеством параметров:

>>> ", line 1, in TypeError: f1() takes exactly 1 argument (0 given)

Теперь методы. Методы - это функции с небольшим количеством специй. Здесь есть дескрипторы. Как описано в модели данных , A.f1 и A().f1 переводятся в A.__dict__["f1"].__get__(None, A) и type(a).__dict__["f1"].__get__(a, type(a)) соответственно. И результаты этих __get__ отличаются от функции raw f1 . Эти объекты являются обертками вокруг исходного f1 и содержат некоторую дополнительную логику.

В случае unbound method эта логика включает проверку того, является ли первый аргумент экземпляром A:

>>> f = A.f1 >>> f() Traceback (most recent call last): File "", line 1, in TypeError: unbound method f1() must be called with A instance as first argument (got nothing instead) >>> f(1) Traceback (most recent call last): File "", line 1, in TypeError: unbound method f1() must be called with A instance as first argument (got int instance instead)

Если эта проверка завершается успешно, она выполняет оригинальный f1 с этим экземпляром в качестве первого аргумента:

>>> f(A()) <__main__.A object at 0x800f238d0>

Обратите внимание, что атрибут im_self равен None:

>>> f.im_self is None True

В случае bound method эта логика немедленно поставляет исходный f1 с экземпляром A который был создан (этот экземпляр фактически хранится в im_self):

>>> f = A().f1 >>> f.im_self <__main__.A object at 0x800f23950> >>> f() <__main__.A object at 0x800f23950>

Таким образом, bound означает, что базовая функция привязана к некоторому экземпляру. unbound означает, что он все еще связан, но только с классом.

2018-12-11T00:00Z

Моя интерпретация такова.

Фрагменты Function класса:

Class Function(object): . . . def __get__(self, obj, objtype=None): "Simulate func_descr_get() in Objects/funcobject.c" if obj is None: return self return types.MethodType(self, obj)

Class Function(object): . . . def __get__(self, obj, objtype=None): "Simulate func_descr_get() in Objects/funcobject.c" return types.MethodType(self, obj, objtype)

  1. Если функция вызывается без класса или экземпляра, это простая функция.
  2. Если функция вызывается из класса или экземпляра, ее __get__ вызывается для извлечения завернутой функции:
    а. Bx совпадает с B.__dict__["x"].__get__(None, B) . В Python 3 это возвращает обычную функцию. В Python 2 это возвращает несвязанную функцию.

    б. bx аналогичен type(b).__dict__["x"].__get__(b, type(b) . Это вернет связанный метод как в Python 2, так и в Python 3, что означает, что self будет неявно передан в качестве первого аргумента.

Мы немного поговорили, теперь поговорим о функциях и методах строк .

Я постарался собрать здесь все строковые методы и функции, но если я что-то забыл - поправляйте.

Базовые операции

    Конкатенация (сложение)

    >>> S1 = "spam" >>> S2 = "eggs" >>> print (S1 + S2 ) "spameggs"
  • Дублирование строки

    >>> print ("spam" * 3 ) spamspamspam
  • Длина строки (функция len)

    >>> len ("spam" ) 4
  • Доступ по индексу

    >>> S = "spam" >>> S [ 0 ] "s" >>> S [ 2 ] "a" >>> S [ - 2 ] "a"

    Как видно из примера, в Python возможен и доступ по отрицательному индексу, при этом отсчет идет от конца строки.

  • Извлечение среза

    Оператор извлечения среза: . X – начало среза, а Y – окончание;

    символ с номером Y в срез не входит. По умолчанию первый индекс равен 0, а второй - длине строки.

    >>> s = "spameggs" >>> s [ 3 : 5 ] "me" >>> s [ 2 : - 2 ] "ameg" >>> s [: 6 ] "spameg" >>> s [ 1 :] "pameggs" >>> s [:] "spameggs"

    Кроме того, можно задать шаг, с которым нужно извлекать срез.

    >>> s [:: - 1 ] "sggemaps" >>> s [ 3 : 5 : - 1 ] "" >>> s [ 2 :: 2 ] "aeg"

Другие функции и методы строк

При вызове методов необходимо помнить, что строки в Python относятся к категории неизменяемых последовательностей, то есть все функции и методы могут лишь создавать новую строку.

>>> s = "spam" >>> s [ 1 ] = "b" Traceback (most recent call last): File "", line 1, in s = "b" TypeError : "str" object does not support item assignment >>> s = s [ 0 ] + "b" + s [ 2 :] >>> s "sbam"

Поэтому все строковые методы возвращают новую строку, которую потом следует присвоить переменной.

Таблица "Функции и методы строк"

Функция или метод Назначение
S = "str"; S = "str"; S = """str"""; S = """str"""
S = "s\np\ta\nbbb" Экранированные последовательности
S = r"C:\temp\new" Неформатированные строки (подавляют экранирование)
S = b"byte" Строка
S1 + S2 Конкатенация (сложение строк)
S1 * 3 Повторение строки
S[i] Обращение по индексу
S Извлечение среза
len (S) Длина строки
S.find (str, ,) Поиск подстроки в строке. Возвращает номер первого вхождения или -1
S.rfind (str, ,) Поиск подстроки в строке. Возвращает номер последнего вхождения или -1
S.index (str, ,) Поиск подстроки в строке. Возвращает номер первого вхождения или вызывает ValueError
S.rindex (str, ,) Поиск подстроки в строке. Возвращает номер последнего вхождения или вызывает ValueError
S.replace (шаблон, замена) Замена шаблона
S.split (символ) Разбиение строки по разделителю
S.isdigit () Состоит ли строка из цифр
S.isalpha () Состоит ли строка из букв
S.isalnum () Состоит ли строка из цифр или букв
S.islower () Состоит ли строка из символов в нижнем регистре
S.isupper () Состоит ли строка из символов в верхнем регистре
S.isspace () Состоит ли строка из неотображаемых символов (пробел, символ перевода страницы ("\f"), "новая строка" ("\n"), "перевод каретки" ("\r"), "горизонтальная табуляция" ("\t") и "вертикальная табуляция" ("\v"))
S.istitle () Начинаются ли слова в строке с заглавной буквы
S.upper () Преобразование строки к верхнему регистру
S.lower () Преобразование строки к нижнему регистру
S.startswith (str) Начинается ли строка S с шаблона str
S.endswith (str) Заканчивается ли строка S шаблоном str
S.join (список) Сборка строки из списка с разделителем S
ord (символ) Символ в его код ASCII
chr (число) Код ASCII в символ
S.capitalize () Переводит первый символ строки в верхний регистр, а все остальные в нижний
S.center (width, ) Возвращает отцентрованную строку, по краям которой стоит символ fill (пробел по умолчанию)
S.count (str, ,) Возвращает количество непересекающихся вхождений подстроки в диапазоне [начало, конец] (0 и длина строки по умолчанию)
S.expandtabs () Возвращает копию строки, в которой все символы табуляции заменяются одним или несколькими пробелами, в зависимости от текущего столбца. Если TabSize не указан, размер табуляции полагается равным 8 пробелам
S.lstrip () Удаление пробельных символов в начале строки
S.rstrip () Удаление пробельных символов в конце строки
S.strip () Удаление пробельных символов в начале и в конце строки
S.partition (шаблон) Возвращает кортеж, содержащий часть перед первым шаблоном, сам шаблон, и часть после шаблона. Если шаблон не найден, возвращается кортеж, содержащий саму строку, а затем две пустых строки
S.rpartition (sep) Возвращает кортеж, содержащий часть перед последним шаблоном, сам шаблон, и часть после шаблона. Если шаблон не найден, возвращается кортеж, содержащий две пустых строки, а затем саму строку
S.swapcase () Переводит символы нижнего регистра в верхний, а верхнего – в нижний
S.title () Первую букву каждого слова переводит в верхний регистр, а все остальные в нижний
S.zfill (width) Делает длину строки не меньшей width, по необходимости заполняя первые символы нулями
S.ljust (width, fillchar=" ") Делает длину строки не меньшей width, по необходимости заполняя последние символы символом fillchar
S.rjust (width, fillchar=" ") Делает длину строки не меньшей width, по необходимости заполняя первые символы символом fillchar
S.format (*args, **kwargs)

Признак того, что объект не должен быть классом - если в нём всего 2 метода, и один из них - инициализация, __init__. Каждый раз видя это, подумайте: «наверное, мне нужна просто одна функция».

Перевод доклада Джэка Дидриха , одного из ключевых разработчиков языка Питон. Доклад прозвучал 9 марта 2012 на конференции PyCon US.

Все из вас читали Дзэн Питона , наверное много раз. Вот несколько пунктов из него:

  • Простое лучше сложного
  • Плоское лучше вложенного
  • Важна читаемость
  • Если программу трудно объяснить, она плохая
  • Если программу легко объяснить, возможно, она хорошá

Написал этот текст Тим Питерс. Он умнее и вас, и меня. Сколько вы знаете людей, в честь которых назвали алгоритм сортировки? Вот такой человек написал Дзэн Питона. И все пункты гласят: «Не делай сложно. Делай просто.» Именно об этом и пойдёт речь.

Итак, в первую очередь, не делайте сложно, там, где можно сделать проще. Классы очень сложны или могут быть очень сложны. Но мы всё равно делаем сложно, даже стараясь делать проще. Поэтому в этом докладе мы прочитаем немного кода и узнаем, как заметить, что идём неверным путём, и как выбраться обратно.

На своей работе я говорю коллегам: «Я ненавижу код, и хочу чтобы его было как можно меньше в нашем продукте.» Мы продаём функционал, не код. Покупатели у нас не из-за кода, а из-за широкого функционала. Каждый раз, когда код удаляется, это хорошо. Нас четверо, и в последний год мы перестали считать количество строк в продукте, но продолжаем вводить новый функционал.

Классы

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

Class Greeting(object): def __init__(self, word="Hello"): self.word = word def greet(self, name): return "%s, %s!" % (self.word, name) >>> greeting = Greeting("Hola") >>> greeting.greet("Jorge") Hola, Jorge!

Это не класс, хотя он похож на класс. Имя - существительное, «приветствие». Он принимает аргументы и сохраняет их в __init__. Да, выглядит как класс. У него есть метод, читающий состояние объекта и делающий что-то ещё, как в классах. Внизу написано, как этим классом пользуются: создаём экземпляр Приветствия и затем используем это Приветствие чтобы сделать что-то ещё.

Но это не класс, или он не должен быть классом. Признак этого - в нём всего 2 метода, и один из них - инициализация, __init__. Каждый раз видя это, подумайте: «наверное, мне нужна просто одна функция».

Каждый раз когда из написанного класса вы создаёте всего один экземпляр, используете только раз и тут же выбрасываете, следует думать: «ой, надо бы это отрефакторить! Можно сделать проще, намного проще!»

Def greet(name): ob = Greeting("превед") print ob.greet(name) return

Эта функция состоит из 4 строк кода. А вот как можно сделать то же самое всего за 2 строки:

Def greet(greeting, name): return "%s, %s!" % (greeting, name) import functools greet = functools.partial(greet, "превед") greet("красавчик")

Если вы всё время вызываете функцию с тем же первым аргументом, стандартной библиотеке есть инструмент! functools.partial. Вот посмотрите в код выше: добавляете аргумент, и результат можно вызывать многократно.

Не знаю, у скольких из вас диплом в ИТ, у меня он есть. Я учил такие понятия как

Разделение полномочий
- уменьшение связанности кода
- инкапсуляция
- изоляция реализации

С тех пор как я закончил вуз, 15 лет я этих терминов не употреблял. Слыша эти слова, знайте, вас дурят. Эти термины сами по себе не требуются. Если их используют, люди имеют в виду совершенно разное, что только мешает разговору.

Пример: брюки превращаются...

Многие из вас пользуются в повседневной работе сторонними библиотеками. Каждый раз когда надо пользоваться чужим кодом, первое, что нужно сделать - прочитать его. Ведь неизвестно, что там, какого качества, есть ли у них тесты и так далее. Нужно проверить код прежде чем включать его. Иногда читать код бывает тяжко.

Сторонняя библиотека API, назовём её ShaurMail, включала 1 пакет, 22 модуля, 20 классов и 660 строк кода. Мне пришлось всё это прочитать прежде чем включить в продукт. Но это был их официальный API, поэтому мы пользовались им . Каждый раз когда приходили обновления API, приходилось просматривать диффы, потому что было неизвестно, что они поменяли. Вы посылали патчи - а в обновлении они появились?

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

Что такое злоупотребление классами? Часто люди думают, что им понадобится что-то в будущем.… Не понадобится. Напишите всё, когда потребуется. В библиотеке ШаурМаил есть модуль ШаурХэш, в котором 2 строки кода:

Class ShaurHash(dict): pass

Кто-то решил, что позже понадобится надстройка над словарём. Она не понадобилась, но везде в коде остались строки, как первая:

My_hash = ShaurMail.ShaurHash.ShaurHash(id="cat") d = dict(id="cat") d = {"id": "cat"}

Вторая и третья строки кода - никому не нужно объяснять их. Но там везде повторялась эта мантра «ШаурМаил-ШаурХэш-ШаурХэш». Троекратное повторение слова «Шаур» - ещё один признак излишества. От повторений всем только вред. Вы раздражаете пользователя, заставляя его писать «Шаур» три раза. (Это не настоящее имя компании, а вымышленное.)

Потом они уволили этого парня и наняли того, кто знал, что делает. Вот вторая версия API:

Class API: def __init__(self, key): self.header = dict(apikey=key) def call(self, method, params): request = urllib2.Request(self.url + method + "/" + method, urllib.urlencode(params), self.header) try: response = json.loads(urllib2.urlopen(request).read()) return response except urllib2.HTTPError as error: return dict(Error=str(error))

В той было 660 строк, в этой - 15. Всё, что делает этот код - пользуется методами стандартной библиотеки. Он читается целиком, легко, за секунды, и можно сразу понять, что он делает. Кстати, в нём был ещё набор тестов из 20 строк. Вот как надо писать. Когда они обновляли API, я мог прочесть изменения буквально за пару секунд.

Но и здесь можно заметить проблему. В классе два метода, и один из них - __init__. Авторы этого не скрывали. Второй метод - call, «вызвать». Вот как этим API пользоваться:

ShaurMail.API(key="СЕКРЕТНЫЙ КЛЮЧ").call(("mailing", "statistics"), {"id": 1})

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

ShaurMail.request = ShaurMail.API(key="СЕКРЕТНЫЙ КЛЮЧ").call ShaurMail.request(("mailing", "statistics"), {"id": 1})

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

ShaurMail_API = url = "https://api.shaurmail.com/%s/%s" ShaurMail_API_KEY = "СЕКРЕТНЫЙ КЛЮЧ" def request(noun, verb, **params): headers = {"apikey": ShaurMail_API_KEY} request = urllib2.Request(ShaurMail_API % (noun, verb), urllib.urlencode(params), headers) return json.loads(urllib2.urlopen(request).read())

Он вообще не создаёт файлов в нашем проекте, потому что я вставил его в тот модуль, где он используется. Он делает всё, что делал 15-строковый API, и всё, что делал 660-строковый API.

Вот с чего мы начали и к чему пришли:

  • 1 пакет + 20 модулей => 1 модуль
  • 20 классов => 1 класс => 0 классов
  • 130 методов => 2 метода => 1 функция
  • 660 строк кода => 15 строк => 5 строк

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

Стандартная библиотека

Кто пришёл из языка Java, возможно, считает, что пространства имён нужны для таксономии. Это неверно. Они нужны чтобы предотвратить совпадения имён. Если у вас глубокие иерархии пространств, это никому ничего не даёт. ShaurMail.ShaurHash.ShaurHash - всего лишь лишние слова, которые людям надо помнить и писать.

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

К нашему стыду, вот пример из нашего же кода, и те же грехи видно и здесь:

Services.crawler.crawlerexceptions.ArticleNotFoundException

Пакет, в котором модуль из 2 строк, класс исключения и «pass». Чтобы использовать это исключение, надо дважды написать «crawler», дважды слово «exception». Имя ArticleNotFoundException само себя повторяет. Так не надо. Если вы называете исключения, пусть это будет EmptyBeer, BeerError, BeerNotFound, но BeerNotFoundError - это уже много.

Можно просто пользоваться исключениями из стандартной библиотеки. Они понятны всем. Если только вам не нужно выловить какое-то специфическое состояние, LookupError вполне подойдёт. Если вы получили отлуп по почте, всё равно придётся его читать, поэтому неважно, как называется исключение.

Кроме того, исключения в коде обычно идут после raise и except, и всем сразу понятно, что это исключения. Поэтому не нужно добавлять «Exception» в название.

В стандартной библиотеке Питона есть и ржавые детали, но она - очень хороший пример организации кода:

  • 200 000 строк кода
  • 200 модулей верхнего уровня
  • в среднем по 10 файлов в пакете
  • 165 исключений

10 файлов в пакете - это много, но только из-за некоторых сторонних проектов, добавленных в библиотеку, где были пакеты из всего 2 файлов. Если вам вздумается создать новое исключение, подумайте лучше, ведь в стандартной библиотеке обошлись 1 исключением на 1200 строк кода.

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

Единственное исключение в библиотеке Питона - модуль heapq. Heap queue, «очередь в куче» - это массив, который всегда отсортирован. В модуле heapq десяток методов, и они все работают с той же «кучей». Первый аргумент всегда остаётся тем же, что значит, здесь действительно напрашивается класс.

Heapify(data) pushleft(data, item) popleft(data) pushright(data, item) popright(data)
и т.д.

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

Class Heap(object): def __init__(self, data=None, key=lambda x: None): self.heap = data or heapq.heapify(self.heap) self.key = key def pushleft(self, item): if self.key: item = (self.key(item), item) heapq.pushleft(self.heap, item) def popleft(self): return heapq.popleft(self.heap)

Классы разрастаются как сорняки

Состояние OAuth в Питоне - неважное. Опять же, есть сторонние библиотеки, и прежде чем использовать в своём проекте, их нужно прочесть.

Я пытался использовать сокращатель урлов от Гугла: мне нужно было взять урлы и просто сократить их. У Гугла есть проект, в котором 10 000 строк кода. 115 модулей и 207 классов. Я написал отповедь об этом в Гугле+ , но мало кто её видел, а Гвидо (Ван Россум - прим. пер.) прокомментировал: «Я снимаю с себя ответственность за гугловский код API.» 10 000 строк кода - там же обязательно найдётся какая-нибудь дрянь вроде ШаурМэйла. Вот, например, класс Flow («поток»), от которого наследуют другие.

Class Flow(object): pass class Storage(object): def put(self, data): _abstract() def get(self): _abstract() def _abstract(): raise NotImplementedError

Он пустой. Но у него есть свой модуль, и каждый раз читая наследующий его класс, надо сходить, проверить тот файл и снова убедиться, что тот класс пуст. Кто-то глядел в будущее и решил: «Напишу-ка я 3 строчки кода сейчас, чтобы в будущем эти 3 строчки не менять.» И отнял время у всех, кто читает его библиотеку. Есть ещё класс Хранилище, (Storage) который почти ничего не делает. В нём правильно обрабатываются ошибки, используя стандартные исключения, но им делают алиасы, и опять же нужно ходить читать их код, чтобы выяснить, как это работает.

Чтобы внедрить OAuth2 мне понадобилась неделя. Пару дней заняло чтение десяти тысяч строк кода, после чего я стал искать другие библиотеки. Нашёл python-oauth2. Это вторая версия python-oauth, но она на самом деле не умеет работать с OAuth2, что не сразу удалось выяснить. Впрочем, эта библиотека немного лучше гугловской: только 540 строк и 15 классов.

Я переписал её ещё проще и назвал python-foauth2 . 135 строк кода и 3 класса, и то всё равно много, я не достаточно её отрефакторил. Вот один из этих трёх классов:

Class Error(Exception): pass

Срамота!

Жизнь

Последний пример. Все вы видели игру «Жизнь» Конвэя , даже если не знаете её имени. Есть клетчатое поле, каждый ход вы считаете для каждой клетки соседние, и в зависимости от них она будет либо живой, либо мёртвой. И получаются такие красивые устойчивые узоры, как планер: клетки впереди оживают, а сзади умирают, и планер как будто летит по полю.

Игра «жизнь» очень проста: поле и пара правил. Мы задаём эту задачу на собеседовании, потому что если вы не умеете такого - нам не о чем разговаривать. Многие сразу же говорят «Клетка - существительное. Класс надо.» Какие свойства в этом классе? Место, живая или нет, состояние в следующий ход, всё? Ещё есть соседи. Потом начинают описывать поле. Поле - это множество клеток, поэтому это сетка, у неё метод «подсчитать», который обсчитывает клетки внутри.

Class Cell(object): def __init__(self, x, y, alive=True): self.x = x self.y = y self.alive = alive self.next = None def neigbors(self): for i, j in itertools.product(range(-1, 2), repeat=2): if (i, j) != (0, 0): yield (self.x + i, self.y + j) class Board(object): def __init__(self): self.cells = {} # { (x, y): Cell() } def advance(self): for (x, y), cell in self.cells.items(): alive_neighbors = len(cell.neighbors) cell.next = (alive_neighbors == 3 or (alive_neighbors == 2 and cell.alive))

На этом месте надо сказать «стоп»: у нас есть класс Поле, в котором 2 метода: __init__ и «сделать ход». В нём одно свойство - словарь, значит со словарём и надо работать. Заметьте, что не надо хранить соседей точки, они уже и так есть в словаре. Живая точка или нет - это просто булево значение, поэтому будем хранить координаты только живых клеток. А раз в словаре хранятся только True, нужен не словарь а просто множество (set) координат. Наконец, новое состояние не нужно, можно просто заново создать список живых клеток.

Def neigbors(point): x, y = point for i, j in itertools.product(range(-1, 2), repeat=2): if any((i, j)): yield (x + i, y + j) def advance(board): newstate = set() recalc = board | set(itertools.chain(*map(neighbors, board))) for point in recalc: count = sum((neigh in board) for neigh in neighbors(point)) if count == 3 or (count == 2 and point in board): newstate.add(point) return newstate glider = set([(0, 0), (1, 0), (2, 0), (0, 1), (1, 2)]) for i in range(1000): glider = advance(glider) print glider

Получается очень простая, сжатая реализация игры. Двух классов тут не надо. Внизу - координаты планера, их вставляют в поле, и планер летит. Всё. Это полная реализация игры «жизнь».

Резюме

1. Если вы видите класс с двумя методами, включая __init__, это не класс.
2. Не создавайте новых исключений, если они не нужны (а они не нужны).
3. Упрощайте жёстче.

От переводчика: в комментариях я вижу, что многие восприняли доклад как полное отрицание ООП. Это ошибка. Пункт 1 из итогов чётко говорит, что такое не класс. Классы нужны, но суть доклада - в том, что не нужно ими злоупотреблять.

Оператор Описание Примеры
+ Сложение - Суммирует значения слева и справа от оператора

15 + 5 в результате будет 20
20 + -3 в результате будет 17
13.4 + 7 в результате будет 20.4

- Вычитание - Вычитает правый операнд из левого 15 - 5 в результате будет 10
20 - -3 в результате будет 23
13.4 - 7 в результате будет 6.4
* Умножение - Перемножает операнды 5 * 5 в результате будет 25
7 * 3.2 в результате будет 22.4
-3 * 12 в результате будет -36
/ Деление - Делит левый операнд на правый 15 / 5 в результате будет 3
5 / 2 в результате будет 2 (В Python 2.x версии при делении двух целых чисел результат будет целое число)
5.0 / 2 в результате будет 2.5 (Чтобы получить "правильный" результат хотя бы один операнд должен быть float)
% Деление по модулю - Делит левый операнд на правый и возвращает остаток. 6 % 2 в результате будет 0
7 % 2 в результате будет 1
13.2 % 5 в результате 3.2
** Возведение в степень - возводит левый операнд в степень правого 5 ** 2 в результате будет 25
2 ** 3 в результате будет 8
-3 ** 2 в результате будет -9
// Целочисленное деление - Деление в котором возвращается только целая часть результата. Часть после запятой отбрасывается. 12 // 5 в результате будет 2
4 // 3 в результате будет 1
25 // 6 в результате будет 4

Операторы сравнения в Python:

Оператор Описание Примеры
== Проверяет равны ли оба операнда. Если да, то условие становится истинным. 5 == 5 в результате будет True
True == False в результате будет False
"hello" == "hello" в результате будет True
!= 12 != 5 в результате будет True
False != False в результате будет False
"hi" != "Hi" в результате будет True
<> Проверяет равны ли оба операнда. Если нет, то условие становится истинным.

12 <> 5 в результате будет True. Похоже на оператор!=

> Проверяет больше ли значение левого операнда, чем значение правого. Если да, то условие становится истинным. 5 > 2 в результате будет True.
True > False в результате будет True.
"A" > "B" в результате будет False.
< Проверяет меньше ли значение левого операнда, чем значение правого. Если да, то условие становится истинным. 3 < 5 в результате будет True.
True < False в результате будет False.
"A" < "B" в результате будет True.
>= Проверяет больше или равно значение левого операнда, чем значение правого. Если да, то условие становится истинным. 1 >= 1 в результате будет True.
23 >= 3.2 в результате будет True.
"C" >= "D" в результате будет False.
<= Проверяет меньше или равно значение левого операнда, чем значение правого. Если да, то условие становится истинным. 4 <= 5 в результате будет True.
0 <= 0.0 в результате будет True.
-0.001 <= -36 в результате будет False.

Операторы присваивания в Python:

Оператор Описание Примеры
= Присваивает значение правого операнда левому. c = 23 присвоит переменной с значение 23
+= Прибавит значение правого операнда к левому и присвоит эту сумму левому операнду.

с = 5
а = 2
с += а равносильно: с = с + а. с будет равно 7

-= Отнимает значение правого операнда от левого и присваивает результат левому операнду.

с = 5
а = 2
с -= а равносильно: с = с - а. с будет равно 3

*= Умножает правый операнд с левым и присваивает результат левому операнду.

с = 5
а = 2
с *= а равносильно: с = с * а. c будет равно 10

/= Делит левый операнд на правый и присваивает результат левому операнду. с = 10
а = 2
с /= а равносильно: с = с / а. c будет равно 5
%= Делит по модулю операнды и присваивает результат левому. с = 5
а = 2
с %= а равносильно: с = с % а. c будет равно 1
**= Возводит в левый операнд в степень правого и присваивает результат левому операнду. с = 3
а = 2
с **= а равносильно: с = с ** а. c будет равно 9
//= Производит целочисленное деление левого операнда на правый и присваивает результат левому операнду. с = 11
а = 2
с //= а равносильно: с = с // а. c будет равно 5

Побитовые операторы в Python:

Побитовые операторы предназначены для работы с данными в битовом (двоичном) формате. Предположим, что у нас есть два числа a = 60; и b = 13. В двоичном формате они будут иметь следующий вид:

Оператор Описание Примеры
& Бинарный "И" оператор, копирует бит в результат только если бит присутствует в обоих операндах. (a & b) даст нам 12, которое в двоичном формате выглядит так 0000 1100
| Бинарный "ИЛИ" оператор копирует бит, если тот присутствует в хотя бы в одном операнде. (a | b) даст нам 61, в двоичном формате 0011 1101
^ Бинарный "Исключительное ИЛИ" оператор копирует бит только если бит присутствует в одном из операндов, но не в обоих сразу. (a ^ b) даст нам 49, в двоичном формате 0011 0001
~ Бинарный комплиментарный оператор. Является унарным (то есть ему нужен только один операнд) меняет биты на обратные, там где была единица становиться ноль и наоборот. (~a) даст в результате -61, в двоичном формате выглядит 1100 0011.
<< Побитовый сдвиг влево. Значение левого операнда "сдвигается" влево на количество бит указанных в правом операнде. a << 2 в результате даст 240, в двоичном формате 1111 0000
>> Побитовый сдвиг вправо. Значение левого операнда "сдвигается" вправо на количество бит указанных в правом операнде. a >> 2 даст 15, в двоичном формате 0000 1111

Логические операторы в Python:

Оператор Описание Примеры
and Логический оператор "И". Условие будет истинным если оба операнда истина.

True and True равно True.
True and False равно False.
False and True равно False.
False and False равно False.

or Логический оператор "ИЛИ". Если хотя бы один из операндов истинный, то и все выражение будет истинным. True or True равно True.
True or False равно True.
False or True равно True.
False or False равно False.
not Логический оператор "НЕ". Изменяет логическое значение операнда на противоположное. not True равно False.
not False равно True.

Операторы членства в Python:

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

Операторы тождественности в Python:

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

Приоритет операторов в Python

В следующей таблице описан приоритет выполнения операторов в Python от наивысшего (выполняется в первую очередь) до наинизшего.

Оператор Описание
** Возведение в степень
~ + - Комплиментарный оператор
* / % // Умножение, деление, деление по модулю, целочисленное деление.
+ - Сложение и вычитание.
>> << Побитовый сдвиг вправо и побитовый сдвиг влево.
& Бинарный "И".
^ | Бинарный "Исключительное ИЛИ" и бинарный "ИЛИ"
<= < > >= Операторы сравнения
<> == != Операторы равенства
= %= /= //= -= += *= **= Операторы присваивания
is is not Тождественные операторы
in not in Операторы членства
not or and Логические операторы