автор: Дьяконов Александр www.dyakonov.org
для поддержки курсов автора, в частности https://github.com/Dyakonov/IML
a = 1
b = 2.5
print (a + b)
print (a.__add__(b)) # НЕ РАБОТАЕТ
3.5 NotImplemented
# в Python всё - объекты. Имеют id и значение
a = 1
b = [1, 2]
print (id(a), id(b))
a = a + 1
b.append(3) # id не изменится
print (id(a), id(b))
del a # удаление объекта
# a # будет ошибка
1578102544 73531656 1578102576 73531656
# определение класса
# первый аргумент всех методов - экземпляр класса
class MyGraph:
def __init__(self, V, E): # конструктор (деструктор - __del__)
self.vertices = set(V)
self.edges = set(E)
def add_vertex(self, v): # метод - функция, объявленная в теле класса
self.vertices.add(v)
def add_edge(self, e):
self.vertices.add(e[0])
self.vertices.add(e[1])
self.edges.add(e)
def __str__(self): # представление в виде строки
return ("%s; %s" % (self.vertices, self.edges))
g = MyGraph([1,2,3], [(1,2), (1,3)])
g.add_edge((3,4))
print (g)
{1, 2, 3, 4}; {(1, 2), (1, 3), (3, 4)}
print (g.vertices)
print (g.__getattribute__('vertices'))
g.__setattr__('vertices', set([1,2,3,4,5]))
print (g)
{1, 2, 3, 4} {1, 2, 3, 4} {1, 2, 3, 4, 5}; {(1, 2), (1, 3), (3, 4)}
class X:
a = 0 # обычный атрибут
_b = 1 # не желательно пользоваться, но доступен
__c = 2 # доступен под другим именем ('_X__c')
print (dir(X))
print (X.__dict__) # все атрибуты в виде словаря
['_X__c', '__doc__', '__module__', '_b', 'a'] {'a': 0, '__module__': '__main__', '_X__c': 2, '_b': 1, '__doc__': None}
# множественное наследование
class A:
def make(self, x):
print(x+1)
class B:
def make(self, x):
print(x-1)
class C(A, B):
pass
c = C()
c.make(2)
print (C.__mro__) # в таком порядке ищутся методы
3 (<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>)
# класс
class Human(object):
species = "H. sapiens" # атрибут
# инициализатор
# когда класс инициализируется
def __init__(self, name):
self.name = name # инициализация атрибута
self.age = 0 # инициализация свойства
# метод класса, первый аргумент - self
def say(self, msg):
return "{0}: {1}".format(self.name, msg)
# общий метод для всех экземпляров
# первый аргумент - кто вызвал
@classmethod
def get_species(cls):
return cls.species
# вызывается без ссылки на вызвавшего
@staticmethod
def grunt():
return "статика..."
# свойство - превращает метод в атрибут
@property
def age(self):
return self._age
# для присваивания свойству
@age.setter
def age(self, age):
self._age = age
# для удаления свойства
@age.deleter
def age(self):
del self._age
# инициализация
i = Human(name="Иван")
print (i.say("привет"))
j = Human("Сергей")
print (j.say("пока"))
print (i.get_species())
# меняем атрибут общий - для всего класса
Human.species = "H. neanderthalensis"
print (i.get_species(), i.species)
print (j.get_species(), j.species)
print (Human.grunt()) # статический метод
i.age = 42 # свойство
print (i.age)
del i.age
# i.age # будет исключение
Иван: привет Сергей: пока H. sapiens H. neanderthalensis H. neanderthalensis H. neanderthalensis H. neanderthalensis статика... 42
class Myclass:
__slots__ = ['name1', 'name2'] # указываем ВСЕ возможные атрибуты
# -- занимает меньше памяти
c = Myclass()
c.name1 = 10
c.name2 = lambda x: x * x
c.name2(2)
#c.name3 = 20 # должна быть ошибка (в Python2 - нет)
#c.name3
4
a = [1, 2]
b = a
print (b)
a[0] = 3
print (b) # a и b идентичны
del a
print (b) # но b остаётся!
[1, 2] [3, 2] [3, 2]
# чтобы Unicode-литералы правильно воспринимались интерпретатором, в начале программы укажите
# -*- coding: koi8 -r -*-
# -*- coding: cp1251 -*-
# 2я запись - в Win
import encodings.aliases
print (encodings.aliases.aliases.values()) # возможные кодировки
dict_values(['mac_greek', 'zlib_codec', 'iso8859_15', 'cp1255', 'iso8859_9', 'shift_jis', 'gb2312', 'cp500', 'cp1140', 'iso8859_8', 'hp_roman8', 'cp037', 'cp949', 'cp864', 'euc_kr', 'gbk', 'cp852', 'iso8859_7', 'cp037', 'latin_1', 'utf_7', 'cp860', 'cp1253', 'iso8859_5', 'cp1125', 'big5', 'shift_jis_2004', 'cp850', 'utf_16_be', 'cp932', 'utf_8', 'iso2022_jp', 'iso8859_16', 'cp500', 'utf_8', 'ascii', 'iso8859_14', 'latin_1', 'base64_codec', 'gb2312', 'cp949', 'hp_roman8', 'euc_jis_2004', 'cp1258', 'quopri_codec', 'uu_codec', 'mac_turkish', 'euc_kr', 'iso8859_14', 'rot_13', 'euc_jp', 'cp1254', 'iso8859_8', 'cp1253', 'ptcp154', 'cp273', 'iso8859_14', 'cp1125', 'iso8859_14', 'cp424', 'big5hkscs', 'cp500', 'iso8859_2', 'cp437', 'hz', 'gbk', 'euc_jp', 'cp852', 'iso2022_jp_ext', 'euc_kr', 'iso8859_15', 'utf_7', 'utf_8', 'cp850', 'shift_jisx0213', 'euc_kr', 'cp437', 'iso8859_11', 'cp855', 'cp861', 'latin_1', 'cp1026', 'cp273', 'iso8859_16', 'shift_jis', 'cp932', 'iso8859_8', 'cp865', 'tis_620', 'iso8859_6', 'cp866', 'utf_8', 'iso8859_13', 'iso8859_16', 'iso8859_14', 'iso2022_jp_1', 'mac_iceland', 'iso8859_6', 'johab', 'iso8859_9', 'cp424', 'cp273', 'iso2022_jp_2004', 'ascii', 'utf_16_le', 'iso2022_jp_1', 'cp857', 'cp950', 'utf_32_le', 'cp1252', 'iso8859_3', 'cp037', 'gb2312', 'hz', 'cp1250', 'utf_16_le', 'iso8859_2', 'cp855', 'iso8859_7', 'iso8859_10', 'cp1256', 'iso8859_8', 'cp857', 'iso8859_14', 'cp437', 'iso8859_10', 'iso8859_2', 'iso8859_9', 'cp857', 'gb2312', 'iso8859_6', 'kz1048', 'iso8859_6', 'hex_codec', 'iso8859_7', 'euc_jis_2004', 'cp1125', 'cp869', 'zlib_codec', 'cp775', 'iso8859_3', 'iso8859_3', 'cp1257', 'iso8859_4', 'iso2022_jp_2', 'kz1048', 'shift_jis', 'cp1255', 'cp1254', 'iso8859_10', 'iso8859_10', 'cp424', 'iso8859_8', 'cp775', 'tis_620', 'latin_1', 'iso8859_3', 'iso8859_5', 'mac_roman', 'kz1048', 'cp949', 'cp037', 'cp037', 'cp1250', 'iso2022_jp_3', 'gb2312', 'latin_1', 'cp424', 'tis_620', 'euc_kr', 'ascii', 'iso8859_13', 'euc_jis_2004', 'iso8859_10', 'cp850', 'utf_16_be', 'ascii', 'iso8859_4', 'ptcp154', 'iso8859_4', 'iso8859_16', 'cp862', 'euc_jisx0213', 'iso8859_7', 'ascii', 'iso2022_kr', 'cp864', 'tis_620', 'utf_32_be', 'cp500', 'latin_1', 'big5', 'ascii', 'iso2022_jp_2004', 'cp037', 'iso2022_jp_2', 'iso8859_11', 'iso2022_jp_ext', 'big5', 'iso8859_9', 'cp1026', 'gb2312', 'cp862', 'cp861', 'latin_1', 'cp866', 'iso2022_jp', 'cp500', 'shift_jis_2004', 'cp860', 'cp950', 'koi8_r', 'cp1251', 'cp863', 'cp852', 'latin_1', 'gb2312', 'cp858', 'euc_kr', 'gb18030', 'cp932', 'utf_8', 'cp1125', 'cp037', 'base64_codec', 'ptcp154', 'cp1251', 'cp869', 'cp860', 'cp858', 'ptcp154', 'cp1256', 'iso8859_4', 'gb2312', 'iso8859_2', 'iso8859_9', 'quopri_codec', 'bz2_codec', 'cp865', 'johab', 'ascii', 'ascii', 'cp1258', 'mac_latin2', 'iso2022_kr', 'cp037', 'utf_7', 'mac_cyrillic', 'iso8859_9', 'cp861', 'iso8859_10', 'iso8859_5', 'utf_16', 'latin_1', 'iso8859_15', 'latin_1', 'cp863', 'iso8859_2', 'iso8859_6', 'utf_32', 'iso2022_jp', 'cp869', 'ascii', 'iso8859_4', 'cp864', 'cp863', 'cp862', 'gb2312', 'cp861', 'cp932', 'cp855', 'utf_32', 'ascii', 'mac_roman', 'quopri_codec', 'iso8859_6', 'ascii', 'iso8859_11', 'mac_latin2', 'euc_kr', 'cp869', 'iso8859_2', 'cp866', 'iso8859_4', 'ascii', 'shift_jisx0213', 'shift_jisx0213', 'cp1140', 'utf_16', 'iso8859_5', 'cp858', 'iso8859_5', 'shift_jis', 'mbcs', 'iso2022_jp_3', 'iso8859_7', 'cp1257', 'hz', 'iso8859_7', 'iso8859_6', 'big5hkscs', 'tis_620', 'cp865', 'tactis', 'iso2022_kr', 'euc_kr', 'hp_roman8', 'shift_jis', 'cp1026', 'iso8859_3', 'latin_1', 'iso8859_16', 'iso8859_3', 'euc_jp', 'shift_jis_2004', 'iso8859_7', 'cp775', 'latin_1', 'gbk', 'cp1252', 'iso8859_7', 'iso8859_13'])
# подсчёт числа объектов соответствующего класса
class Counter:
Count = 0 # счётчик
def __init__(self, name): # внимание к отступам
self.name = name # обращение через self
Counter.Count += 1
print (name, ' создан, счётчик =', Counter.Count)
# Counter.Count - у класса, а не объекта!
def __del__(self):
Counter.Count -= 1
print (self.name, 'удалён, счётчик = ', Counter.Count)
if Counter.Count == 0:
print ('Больше объектов нет...')
x = Counter("First")
y = Counter("Second")
del x
z = Counter("Third")
del z
# если запустить второй раз - какая-то ерунда...
First создан, счётчик = 1 Second создан, счётчик = 2 First удалён, счётчик = 1 Third создан, счётчик = 2 Third удалён, счётчик = 1
# del x
# del y
del Counter
Метакласс - класс для создания других классов (его экземпляры тоже классы)
Пример - класс type (все классы это его экземпляры)
зачем нужны (не будем)
class Anyclass:
x = 10
print (type(Anyclass))
class Nextclass(Anyclass): # Python 3 (metaclass = Anyclass):
y = 20
print (type(Nextclass))
<class 'type'> <class 'type'>
class C: pass
# эквивалентная запись:
С = type('C', (), {})
# type - дефолтный метакласс для создания классов
def myprint(self):
print("список: ", self)
# такой способ создания класса
MyList = type('MyList', (list,), dict(x=10, myprint=myprint))
ml = MyList()
ml.append("one")
ml.append("two")
ml.myprint()
список: ['one', 'two']
class MyClass: pass
# это класс!
MyClass.field = 10
MyClass.method = lambda x: "Привет, мир!"
x = MyClass()
x.field2 = 5
x.method2 = lambda x: "Пока, мир!"
print (x.field, x.method(), x.field2, x.method2(None))
# x.method2 - нужен аргумент
10 Привет, мир! 5 Пока, мир!
# есть атрибуты экземпляра
# а есть атрибуты класса
class MyClass: pass
MyClass.field = 10
x = MyClass()
y = MyClass()
MyClass.field = 5 # модификация произойдёт во всех объектах
print (x.field, y.field)
5 5
# много ещё чего про метаклассы - не стал писать
# ???? - см. Python 3
class Path:
def __init__(self, directory):
self.directory = directory
def __repr__(self):
return "Path({})".format(self.directory)
@property
def parent(self):
return Path(dirname(self.directory))
p = Path("./main/file.txt")
p
Path(./main/file.txt)
class Myclass:
val = 0
def __getattr__(self, name): # __getattr__ - при вызове несуществующего атрибута
return name
m = Myclass()
print (m.my_attr)
my_attr 0 val_other
class Myclass:
val = 0
m = Myclass()
setattr(m, "val", 2) # getattr - безопасное добавление атрибута
# setattr(m, "val_other", 3.0) # m.val_other = 3.0 создастся новый атрибут
print (getattr(m, "val")) # getattr - безопасный вызов атрибута
print (getattr(m, "val_some_other", 1.0))
m.__dict__
2 1.0
{'val': 2, 'val_other': 3.0}
class Vec:
def __init__(self, x=0.0, y=0.0):
self.x = x
self.y = y
def __str__(self): # представление в виде строки
return ("(%s, %s)" % (self.x, self.y))
x = Vec(1, 2)
print (x)
(1, 2)
-- атрибут объекта со скрытым поведеним, которое задают методы в протоколе дескриптора: get(), set(), and delete()
class RevealAccess(object):
"""Пример дескриптора данных:
устанавливает и возвращает значения,
а также пишет сообщения
"""
def __init__(self, initval=None, name='var'):
self.val = initval
self.name = name
def __get__(self, obj, objtype):
print('Выдаём', self.name)
return (self.val + 1) # выдаёт следующее число
def __set__(self, obj, val):
print('Получаем', self.name)
if val<0:
print('Отрицательное значение - будет обнулено')
self.val = 0
else:
self.val = val
class MyClass(object):
x = RevealAccess(10, 'var "x"') # у переменное скрытое поведение
y = 5
m = MyClass()
print (m.x)
m.x = 20
print (m.x)
m.x = -20
print (m.x)
print(m.y)
Выдаём var "x" 11 Получаем var "x" Выдаём var "x" 21 Получаем var "x" Отрицательное значение - будет обнулено Выдаём var "x" 1 5
# свойства в Питоне - "быстрый" дескриптор
# "безопасный класс"
# контролирует значения атрибутов
class SafeClass:
def _get_attr(self):
return self._x
def _set_attr(self, x):
assert x > 0, "необходимо положительное значение"
self._x = x
def _del_attr(self):
del self._x
x = property(_get_attr, _set_attr, _del_attr)
safe = SafeClass()
safe.x = 1
# safe.x = -2 # будет исключение
# ??? пока не понял - МОЖНО УДАЛИТЬ
class NonNegative:
def _get_attr(self):
return self._x
def _set_attr(self, x):
assert x > 0, "необходимо положительное значение"
self._x = x
def _del_attr(self):
del self._x
class SafeClass2:
x = NonNegative()
y = NonNegative()
safe = SafeClass2()
safe.x = -2
safe.y = -2
class SafeClass2:
x = SafeClass()
y = SafeClass()