# Сегодня разбора домашней работы не будет, так как дедлайн продлили еще на один день
# Вот общее замечание для всех
# Старайтесь итерироваться по элементам, а не по индексам!
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) # Нет проблем!
# Конечно, если по смыслу вам нужны сами индексы - используйте их
--------------------------------------------------------------------------- NameError Traceback (most recent call last) <ipython-input-1-3843a1c947db> in <module>() 1 s = ["first", "second", "third"] 2 for i in range(len(s)): ----> 3 do_something_with(s[i]) 4 5 for elem in s: NameError: name 'do_something_with' is not defined
# Другое общее замечание - про ввод строк
# Напоминаю: для чтения одной строки используйте старый добрый input()
s = input()
# Завершающего символа \n в такой строке не будет
Text in one line
s
'Text in one line'
# Если дальше по смыслу надо прочитать много строк (пока не кончатся), используйте 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
# Сегодня мы поговорим про пространства имен и классы
# Это одинаковые или разные иксы? )
x = 17
def f():
x = 42
def g():
x = "abc"
f()
print(x)
17
# Каждое из этих имен x живет в своем "пространстве имен". Они разные
# Вопрос из зала: а можно ли изменить аргумент, переданный как параметр в функцию?
# Ответ: нет, по умолчанию присваивание вводит новое имя:
x = 17
def f(param):
param = 42
f(x)
print(x)
# Впрочем, если бы это был изменяемый объект (список, словарь, множество и т. д.),
# то он передался бы по ссылке, и его содержимое можно было бы менять:
x = [1, 2, 3]
def f(param):
param.append(4)
f(x)
print(x)
[1, 2, 3, 4]
# Но все равно вот так ничего бы не вышло:
x = [1, 2, 3]
def f(param):
param = [1, 2, 3, 4] # Новое локальное имя, не имеющее отношение к аргументу
f(x)
print(x)
[1, 2, 3]
# По умолчанию чтение переменной ищет её имя в объемлющем пространстве имен, если оно не найдено в текущем
# Ключевое слово global позволяет сказать, что присваивание должно брать имя из глобального пространства имен
x = 17
def f():
global x
x = 42
f()
print(x)
42
# А ключевое слово 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
g: 123 f: 123 global: 17
# Класс в первом приближении выглядит просто как пространство имён:
class C:
# Тут могут быть любые операторы
x = 42
s = "Hello"
print("In class C")
def f(a, b):
print(a)
print(b)
In class C
C.x # Обращаемся к атрибутам - класс ведет себя как пространство имён
42
C.s
'Hello'
C.f
<function __main__.C.f>
C.f(1, 2)
1 2
C.y = 3.1415 # Можно смело вводить новые атрибуты
C.y
3.1415
del C.x # И удалять их
C.x
--------------------------------------------------------------------------- AttributeError Traceback (most recent call last) <ipython-input-25-b20e6d542efe> in <module>() ----> 1 C.x AttributeError: type object 'C' has no attribute 'x'
# Но классы - это еще и "шаблоны" для создания объектов
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
)
p = Point(1, 2)
p.x
1
p.y
2
p.color
'black'
q = Point(3, -4)
q.x
3
q.y
-4
q.color
'black'
p.distance(q) # вызываем метод distance у объекта p с параметром q
6.324555320336759
# Фактически это сводится вот к такому вызову:
Point.distance(p, q)
6.324555320336759
# Помните, мы много раз использовали методы стандартных объектов - списков, строк?
# Например:
s = "ABC"
s.lower()
'abc'
# А можно было бы и так:
str.lower(s)
# Мы этим неявно пользуемся, когда передаем str.lower в качестве ключа для сортировки
'abc'
t = Point(5, 6)
t.color
'black'
t.color = "green" # Можно менять атрибуты у самих объектов
Point.color = "white" # А можно поменять у самого класса.
p.color # У p не было своего атрибута color
'white'
t.color # А у t он был
'green'
del t.color # Теперь и у t его нет
t.color # Поэтому его имя ищется в объемлющем пространстве имен - в самом классе
'white'
# Объекты класса вообще можно использовать прямо так
class A:
pass # пустой класс
a = A()
a.x = 1
a.y = 2
b = A()
b.s = "Hello"
b.z = "Good bye"
# Рассмотрим теперь упрощенный пример класса "Рациональное число"
# Вспомогательная функция для вычисления НОД по алгоритму Евклида
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
)
# При желании можно переопределить и другие операторы
p = Rational(-3, 6)
print(abs(p))
1 / 2
p.numerator
-1
p.denominator
2
print(p + p)
-1 / 1