#!/usr/bin/env python # coding: utf-8 # In[ ]: # Сегодня разбора домашней работы не будет, так как дедлайн продлили еще на один день # In[1]: # Вот общее замечание для всех # Старайтесь итерироваться по элементам, а не по индексам! s = ["first", "second", "third"] for i in range(len(s)): do_something_with(s[i]) # Итерация по индексам - так можно, но зачем? for elem in s: do_something_with(elem) # Итерация по элементам - читать такой код приятнее! # Вопрос из зала про итерацию по матрице M = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] for row in M: for elem in row: do_something_with(elem) # Нет проблем! # Конечно, если по смыслу вам нужны сами индексы - используйте их # In[104]: # Другое общее замечание - про ввод строк # Напоминаю: для чтения одной строки используйте старый добрый input() s = input() # Завершающего символа \n в такой строке не будет # In[3]: s # In[4]: # Если дальше по смыслу надо прочитать много строк (пока не кончатся), используйте sys.stdin: import sys for line in sys.stdin: do_something_with(line) line = line.rstrip("\n") # В конце каждой такой строки стоит \n # Или вот так sys.stdin.read().rstrip("\n").split("\n") # (только обратите внимание, что rstrip уберет ВСЕ пустые строки в конце) # Или вообще вот так: lines = sys.stdin.readlines() # Символ \n останется внутри каждой строки # Или так: lines = sys.stdin.read().splitlines() # Символы \n в конце строк обрежутся # Вопрос из зала: как при вводе с клавиатуры завершить ввод? # Ответ: Ctrl+D - это и есть End of file # In[ ]: # Сегодня мы поговорим про пространства имен и классы # In[6]: # Это одинаковые или разные иксы? ) x = 17 def f(): x = 42 def g(): x = "abc" f() print(x) # In[ ]: # Каждое из этих имен x живет в своем "пространстве имен". Они разные # Вопрос из зала: а можно ли изменить аргумент, переданный как параметр в функцию? # Ответ: нет, по умолчанию присваивание вводит новое имя: x = 17 def f(param): param = 42 f(x) print(x) # In[105]: # Впрочем, если бы это был изменяемый объект (список, словарь, множество и т. д.), # то он передался бы по ссылке, и его содержимое можно было бы менять: x = [1, 2, 3] def f(param): param.append(4) f(x) print(x) # In[106]: # Но все равно вот так ничего бы не вышло: x = [1, 2, 3] def f(param): param = [1, 2, 3, 4] # Новое локальное имя, не имеющее отношение к аргументу f(x) print(x) # In[107]: # По умолчанию чтение переменной ищет её имя в объемлющем пространстве имен, если оно не найдено в текущем # Ключевое слово global позволяет сказать, что присваивание должно брать имя из глобального пространства имен x = 17 def f(): global x x = 42 f() print(x) # In[109]: # А ключевое слово nonlocal говорит, что имя надо взять из пространства имен на уровень выше: x = 17 def f(): x = 42 def g(): nonlocal x x = 123 print("g:", x) g() print("f:", x) f() print("global:", x) # Злоупотреблять этим не следует # Старайтесь писать программы, в которых не приходилось бы использовать global и nonlocal # In[111]: # Класс в первом приближении выглядит просто как пространство имён: class C: # Тут могут быть любые операторы x = 42 s = "Hello" print("In class C") def f(a, b): print(a) print(b) # In[112]: C.x # Обращаемся к атрибутам - класс ведет себя как пространство имён # In[19]: C.s # In[20]: C.f # In[21]: C.f(1, 2) # In[113]: C.y = 3.1415 # Можно смело вводить новые атрибуты # In[23]: C.y # In[24]: del C.x # И удалять их # In[25]: C.x # In[116]: # Но классы - это еще и "шаблоны" для создания объектов import math class Point: color = "black" def __init__(self, x, y): # Предопределенное имя - функция, создающая объект # self - сам создаваемый по образцу объект self.x = x self.y = y def distance(self, other): return math.sqrt( (self.x - other.x)**2 + (self.y - other.y)**2 ) # In[117]: p = Point(1, 2) # In[118]: p.x # In[119]: p.y # In[120]: p.color # In[121]: q = Point(3, -4) # In[122]: q.x # In[123]: q.y # In[124]: q.color # In[126]: p.distance(q) # вызываем метод distance у объекта p с параметром q # In[127]: # Фактически это сводится вот к такому вызову: Point.distance(p, q) # In[128]: # Помните, мы много раз использовали методы стандартных объектов - списков, строк? # Например: s = "ABC" s.lower() # In[129]: # А можно было бы и так: str.lower(s) # Мы этим неявно пользуемся, когда передаем str.lower в качестве ключа для сортировки # In[130]: t = Point(5, 6) # In[131]: t.color # In[132]: t.color = "green" # Можно менять атрибуты у самих объектов # In[136]: Point.color = "white" # А можно поменять у самого класса. # In[138]: p.color # У p не было своего атрибута color # In[137]: t.color # А у t он был # In[140]: del t.color # Теперь и у t его нет # In[141]: t.color # Поэтому его имя ищется в объемлющем пространстве имен - в самом классе # In[142]: # Объекты класса вообще можно использовать прямо так class A: pass # пустой класс a = A() a.x = 1 a.y = 2 b = A() b.s = "Hello" b.z = "Good bye" # In[100]: # Рассмотрим теперь упрощенный пример класса "Рациональное число" # Вспомогательная функция для вычисления НОД по алгоритму Евклида def gcd(x, y): # НОД(x, y) = НОД(y, x % y) while y != 0: x, y = y, x % y return x class Rational: def __init__(self, x, y): d = gcd(x, y) self.numerator = x // d # Сократим на общий знаменатель self.denominator = y // d if self.denominator < 0: # Поправим знак числителя и знаменателя при необходимости self.numerator *= -1 self.denominator *= -1 def __str__(self): # Строковое предстваление нашего числа return str(self.numerator) + " / " + str(self.denominator) def __abs__(self): # Модуль числа return Rational(abs(self.numerator), abs(self.denominator)) def __add__(self, other): # Сумма рациональных чисел return Rational( self.numerator * other.denominator + self.denominator * other.numerator, self.denominator * other.denominator ) # При желании можно переопределить и другие операторы # In[143]: p = Rational(-3, 6) # In[144]: print(abs(p)) # In[146]: p.numerator # In[147]: p.denominator # In[148]: print(p + p) # In[ ]: