Математический практикум по Питону.
В данной заметке рассматриваются такие понятия Питона как класс (мешок функций/методов и переменных). Последнее позволяет использовать в смысле наследования мощные классы относящихся к анализу данных, в частности, GenericLikelihoodModel.
Ключевые слова: Питон (python), классы (class), методы (methods), виртуальные (virtual), наследование (inheritance).
Это предварительная версия! Любые замечания приветствуются.
import numpy as np
import matplotlib.pyplot as plt
import scipy.stats as models
Обоснование
# Сформировали массив случайных нормально распределенных чисел.
a = np.random.normal(size=100)
a.shape
(100,)
a_mean = np.mean( a ) # Выборочная средняя.
a_std = np.std( a ) # Выборочная дисперсия.
a_mean, a_std
(-0.13337619791981117, 1.0705143428907786)
# Все тоже самое для другого массива.
b = np.random.normal(size=100)
b.shape
(100,)
b_mean = np.mean( b )
b_std = np.std( b )
b_mean, b_std
(0.16638113349486205, 0.9948082766423356)
(a[:5],(a_mean, a_std))
(array([ 0.34052638, 0.89891682, -0.6697229 , -0.5056998 , 2.59342687]), (-0.13337619791981117, 1.0705143428907786))
Члены класса, поля объекта/записи.
# Создаем класс.
class stats: # Класс по имени stats.
def prt():
print("stats vv")
def calc( self, x ): # Формируем метод класса.
self.mean = np.mean( x ) # Результат сохраняем в объекте self.
self.std = np.std( x ) # Объект будет хранить две величины.
first = stats()
print( first.mean, first.std )
--------------------------------------------------------------------------- AttributeError Traceback (most recent call last) <ipython-input-9-663bc0d2b4b9> in <module> ----> 1 print( first.mean, first.std ) AttributeError: 'stats' object has no attribute 'mean'
stats.calc( first, a )
type(first)
__main__.stats
# Обе статистики теперь вместе.
print( first.mean, first.std )
-0.13337619791981117 1.0705143428907786
# Не спутаем со вторым объектом.
second = stats()
stats.calc( second, b )
print( second.mean, second.std ) # Обе статистики теперь вместе.
0.16638113349486205 0.9948082766423356
tmp = stats()
type(tmp)
__main__.stats
print( tmp.mean, tmp.std )
--------------------------------------------------------------------------- AttributeError Traceback (most recent call last) <ipython-input-17-6acb9ac5497e> in <module> ----> 1 print( tmp.mean, tmp.std ) AttributeError: 'stats' object has no attribute 'mean'
Классы на то и классы, что вызов можно сократить:
first.calc( a )
second.calc( b )
ts = stats()
stats.prt()
stats vv
ts.prt()
--------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-20-8f793b3c329f> in <module> ----> 1 ts.prt() TypeError: prt() takes 0 positional arguments but 1 was given
Печать тоже можно сделать методом
class stats:
def calc( self, x ):
self.mean = np.mean( x )
self.std = np.std( x )
def pri( self ): # Второй метод.
print( self.mean, self.std)
# Попробуем вызвать.
first.pri()
# first пока является классом прошлой версии. Нужно объект заново создать.
--------------------------------------------------------------------------- AttributeError Traceback (most recent call last) <ipython-input-22-de0f4e88fcfc> in <module> 1 # Попробуем вызвать. ----> 2 first.pri() 3 # first пока является классом прошлой версии. Нужно объект заново создать. AttributeError: 'stats' object has no attribute 'pri'
# Объект с чистого листа.
first = stats()
first.calc( a )
first.pri()
-0.13337619791981117 1.0705143428907786
Добавление элемента в объект классв.
# Добавили переменную в объект.
first.qq = 3 # Добавили для конкретного объекта.
# Проверяем значение.
first.qq
3
# Другие объекты затронуты не будут.
second.qq
--------------------------------------------------------------------------- AttributeError Traceback (most recent call last) <ipython-input-26-43d6b61ccda3> in <module> 1 # Другие объекты затронуты не будут. ----> 2 second.qq AttributeError: 'stats' object has no attribute 'qq'
third = stats()
# Создадим объект заново.
third.pri() # Отсутвуют члены класса,
# так как они не были ещё созданы.
--------------------------------------------------------------------------- AttributeError Traceback (most recent call last) <ipython-input-28-d77df748cb78> in <module> 1 # Создадим объект заново. ----> 2 third.pri() # Отсутвуют члены класса, 3 # так как они не были ещё созданы. <ipython-input-21-46984d48bc35> in pri(self) 5 6 def pri( self ): # Второй метод. ----> 7 print( self.mean, self.std) AttributeError: 'stats' object has no attribute 'mean'
# Можно посмотреть, что содержит сам класс.
stats.__dict__ # Под капотом...
mappingproxy({'__module__': '__main__', 'calc': <function __main__.stats.calc(self, x)>, 'pri': <function __main__.stats.pri(self)>, '__dict__': <attribute '__dict__' of 'stats' objects>, '__weakref__': <attribute '__weakref__' of 'stats' objects>, '__doc__': None})
vars(stats)
mappingproxy({'__module__': '__main__', 'calc': <function __main__.stats.calc(self, x)>, 'pri': <function __main__.stats.pri(self)>, '__dict__': <attribute '__dict__' of 'stats' objects>, '__weakref__': <attribute '__weakref__' of 'stats' objects>, '__doc__': None})
# В данном смысле это и мешок,
first.__dict__ # т.е. словарь сущностей в нем находящихся.
{'mean': -0.13337619791981117, 'std': 1.0705143428907786, 'qq': 3}
second.__dict__
{'mean': 0.16638113349486205, 'std': 0.9948082766423356}
third.__dict__ # Собственных сущностей пока не имеет.
{}
vars(first)
{'mean': -0.13337619791981117, 'std': 1.0705143428907786, 'qq': 3}
Хотим добавить метод к классу
# Создаем функцию, которую хотим добавить к объекту.
def get_mean( self ): # Такой тип функции/метода
return self.mean # обычно называют атрибутом.
get_mean(first)
-0.13337619791981117
first.getmm = get_mean # Присвоить удалось.
# Попробуем вызвать.
first.getmm() # Не получилось.
# Не хватает аргумента -- самого объекта.
--------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-38-9306548d8ae5> in <module> 1 # Попробуем вызвать. ----> 2 first.getmm() # Не получилось. 3 # Не хватает аргумента -- самого объекта. TypeError: get_mean() missing 1 required positional argument: 'self'
# Нужно так: какая-то избыточность.
first.getmm( first )
-0.13337619791981117
# Смотрим под капот. Что видим...
first.__dict__ # Видим, что наша функция
# добавилась просто как функция.
{'mean': -0.13337619791981117, 'std': 1.0705143428907786, 'qq': 3, 'getmm': <function __main__.get_mean(self)>}
# Чтобы все получилось.
from types import MethodType
Для создания метода, нужно привязать фнкцию к объекту.
# Используем MethodType для привязки.
first.getmm = MethodType(get_mean, first) # Первый аргумент исходная функция, а второй -- объект.
first.getmm()
-0.13337619791981117
# Теперь видим, что getmm не функция,
first.__dict__ # а метод привязанный к объекту.
{'mean': -0.13337619791981117, 'std': 1.0705143428907786, 'qq': 3, 'getmm': <bound method get_mean of <__main__.stats object at 0x7fc9ed78f5f8>>}
id(first),0x7fd4a01982b0
(140505249281528, 140551195820720)
# Чтобы увидеть, что привязка к тому объекту,
hex( id( first ) ) # что нужно.
'0x7fc9ed78f5f8'
first.getmm()
-0.13337619791981117
# Автоматом конечно для других объектов
second.getmm() # это не распространится.
--------------------------------------------------------------------------- AttributeError Traceback (most recent call last) <ipython-input-47-3c2063bd0772> in <module> 1 # Автоматом конечно для других объектов ----> 2 second.getmm() # это не распространится. AttributeError: 'stats' object has no attribute 'getmm'
# Можно по аналогии добавить метод к классу.
stats.get_mm = MethodType(get_mean, stats)
stats.__dict__
mappingproxy({'__module__': '__main__', 'calc': <function __main__.stats.calc(self, x)>, 'pri': <function __main__.stats.pri(self)>, '__dict__': <attribute '__dict__' of 'stats' objects>, '__weakref__': <attribute '__weakref__' of 'stats' objects>, '__doc__': None, 'get_mm': <bound method get_mean of <class '__main__.stats'>>})
# Хм... не сработало, хотя mean в объекте есть...
first.get_mm()
--------------------------------------------------------------------------- AttributeError Traceback (most recent call last) <ipython-input-50-35a4864c9526> in <module> 1 # Хм... не сработало, хотя mean в объекте есть... ----> 2 first.get_mm() <ipython-input-35-639b92b2fe74> in get_mean(self) 1 # Создаем функцию, которую хотим добавить к объекту. 2 def get_mean( self ): # Такой тип функции/метода ----> 3 return self.mean # обычно называют атрибутом. AttributeError: type object 'stats' has no attribute 'mean'
# Ничего не добавилось сюда.
first.__dict__
{'mean': -0.13337619791981117, 'std': 1.0705143428907786, 'qq': 3, 'getmm': <bound method get_mean of <__main__.stats object at 0x7fc9ed78f5f8>>}
first.getmm()
-0.13337619791981117
ff = stats()
ff.calc(a)
ff.__dict__
{'mean': -0.13337619791981117, 'std': 1.0705143428907786}
ff.get_mm()
--------------------------------------------------------------------------- AttributeError Traceback (most recent call last) <ipython-input-56-26014f7aa356> in <module> ----> 1 ff.get_mm() <ipython-input-35-639b92b2fe74> in get_mean(self) 1 # Создаем функцию, которую хотим добавить к объекту. 2 def get_mean( self ): # Такой тип функции/метода ----> 3 return self.mean # обычно называют атрибутом. AttributeError: type object 'stats' has no attribute 'mean'
Объект начинает свою жизнь с вызова конструктора. Это позволяет построить корретный объект. Так, в нем принято присваивать переменным объекта предопределенные значения.
class stats:
def __init__( self ):# Конструктор!
print('first call!')
self.mean = 0 # Сразу есть,
self.std = 0 # до вызова calc.
def calc( self, x ):
self.mean = np.mean( x )
self.std = np.std( x )
def pri( self ):
print( self.mean, self.std)
forth = stats()
first call!
forth.pri() # Раньше такой вызов давал ошибку.
0 0
class stats:
def __init__( self, x = None ):# Конструкотор,
if x is None: # расчитан на два случая.
print('simple call!')# Данных нет.
self.mean = 0
self.std = 0
return
print('data call!') # Данные поданы.
self.calc( x )
def calc( self, x ):
self.mean = np.mean( x )
self.std = np.std( x )
def pri( self ):
print( self.mean, self.std)
fifth = stats()
fifth.pri()
simple call! 0 0
sixth = stats( a ) # Сразу подали данные.
sixth.pri()
data call! -0.13337619791981117 1.0705143428907786
class Matrix:
def __init__(self, n, m):
self.A = np.zeros((n,m))
def sum(self):
return np.sum(self.A)
def set(self, i, j, v):
self.A[i,j] = v
M = Matrix()
--------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-64-881fc042458d> in <module> ----> 1 M = Matrix() TypeError: __init__() missing 2 required positional arguments: 'n' and 'm'
M = Matrix(2,5)
M.set(0,1,5.5)
M.sum()
5.5
Важно, что ранее объявленные переменные были именно частью self, а не класса. Иначе будет следующий эффект
vars(M)
{'A': array([[0. , 5.5, 0. , 0. , 0. ], [0. , 0. , 0. , 0. , 0. ]])}
class stats:
all_means2 = []
def __init__( self, x = None ):
if x is None:
print('simple call!')
self.mean = 0
self.std = 0
else:
print('data call!')
self.all_means = []
def calc( self, x ):
self.mean = np.mean( x )
self.std = np.std( x )
self.all_means.append( self.mean )
def calc2( self, x ):
# Используем ранее вычисленное значение.
self.all_means2.append( self.mean )
def pri( self ):
print( self.mean, self.std)
stats.__dict__
mappingproxy({'__module__': '__main__', 'all_means2': [], '__init__': <function __main__.stats.__init__(self, x=None)>, 'calc': <function __main__.stats.calc(self, x)>, 'calc2': <function __main__.stats.calc2(self, x)>, 'pri': <function __main__.stats.pri(self)>, '__dict__': <attribute '__dict__' of 'stats' objects>, '__weakref__': <attribute '__weakref__' of 'stats' objects>, '__doc__': None})
first = stats( )
first.calc( a )
second = stats( )
second.calc(b )
print( first.all_means, second.all_means )
# Переменная all_means
simple call! simple call! [-0.13337619791981117] [0.16638113349486205]
first.pri(), second.pri()
-0.13337619791981117 1.0705143428907786 0.16638113349486205 0.9948082766423356
(None, None)
print( first.all_means2, second.all_means2 )#одна и таже для всех объектов.
[] []
first.__dict__
{'mean': -0.13337619791981117, 'std': 1.0705143428907786, 'all_means': [-0.13337619791981117]}
second.__dict__
{'mean': 0.16638113349486205, 'std': 0.9948082766423356, 'all_means': [0.16638113349486205]}
first.calc2( a )
second.calc2( b )
print( first.all_means2, ";", second.all_means2 )
# Переменная all_means одна и таже для всех объектов.
[-0.13337619791981117, 0.16638113349486205] ; [-0.13337619791981117, 0.16638113349486205]
id(first.all_means2), id(second.all_means2)
(140505251381576, 140505251381576)
# Создаем функцию которую зотим добавить к объекту.
def get_all_mean2( self ):
return self.all_means2
first.__dict__ # all_means2 нет в мешке first.
{'mean': -0.13337619791981117, 'std': 1.0705143428907786, 'all_means': [-0.13337619791981117]}
stats.get_mm = MethodType(get_all_mean2, stats) # Дубль два.
first.get_mm() # Теперь сработало! Вывели член класса, но не объекта.
[-0.13337619791981117, 0.16638113349486205]
first.all_means
[-0.13337619791981117]
class stats:
all_means2 = []
def __init__( self, x = None ):
if x is None:
print('simple call!')
self.mean = 0
self.std = 0
else:
print('data call!')
self.all_means = []
def calc( self, x ):
self.mean = np.mean( x )
self.std = np.std( x )
self.all_means.append( self.mean )
def calc2( self, x ):
# Используем ранее вычисленное значение.
self.all_means2.append( self.mean )
def pri( self ):
print( self.mean, self.std)
#!!
def __format__(self, format):
return f'{self.mean}, {self.std}'
first = stats( )
first.calc( a )
second = stats( )
second.calc(b )
print( first.all_means, second.all_means )
# Переменная all_means
simple call! simple call! [-0.13337619791981117] [0.16638113349486205]
f"{first}"
'-0.13337619791981117, 1.0705143428907786'
dd = 2j
dd
2j
Частичное задание аргументов функции
class MyPow:
def __init__(self, t):
self.t = t
def __call__(self, x):
return np.power(x, self.t)
ll = [16, 25, 9]
mypow = MyPow(0.5)
list( map( mypow, ll) )
[4.0, 5.0, 3.0]
Аккумулятор данных
class Summary:
def __init__(self):
self.sum = 0
def __call__(self, x):
self.sum += x
return x>=0
ll = [1, -2, 3, -1]
mm = Summary()
list(filter( lambda x: x>=0, ll))
[1, 3]
list(filter( mm, ll))
[1, 3]
mm.sum
1
Обратный вызов с аргументом
class myCallBack:
def __init__( self, func, *dat):
self.d = dat
self.f = func
def __call__(self, x):
return self.f( *self.d, x )
pw = myCallBack( np.power, 2.)
pw(3)
8.0
list( map( pw, ll) )
[2.0, 0.25, 8.0, 0.5]
Хотим сделать что-то наподобие:
2<3
True
Необходима возможность оперировать множеством.
set1 = {'a', 'b', 'z'}
set1
{'a', 'b', 'z'}
set2 = {'a', 'b', 'f'}
set2
{'a', 'b', 'f'}
Объединение множеств
set1.union(set2)
{'a', 'b', 'f', 'z'}
set1
{'a', 'b', 'z'}
set1 | set2
{'a', 'b', 'f', 'z'}
Пересечение множеств
set1.intersection(set2)
{'a', 'b'}
set1
{'a', 'b', 'z'}
set1 & set2
{'a', 'b'}
set3 = set1.copy()
set3.intersection_update(set2)
set3
{'a', 'b'}
Вычитание
set1 - set2
{'z'}
set2 - set1
{'f'}
set1 ^ set2
{'f', 'z'}
Добавление новых элементов
set1.add('k')
set1
{'a', 'b', 'k', 'z'}
Удалить элемент
set1.discard('b') # remove требует наличие
set1
{'a', 'k', 'z'}
set1.pop()
'k'
set1
{'a', 'z'}
Мономы
# Пусть мономами будут:
mon0 = "xyz"
mon1 = "yz"
mon2 = "x^2yz"
mon3 = "xy^3z"
import re as re
step = dict()
for it in re.finditer( "(([a-z])(\^([1-9]))?)", mon3):
print(it.group(), it.group(2), it.group(4))
if it.group(4) is None:
step[it.group(2)] = 1
else:
step[it.group(2)] = it.group(4)
step
x x None y^3 y 3 z z None
{'x': 1, 'y': '3', 'z': 1}
class Monom:
def __init__(self, mon):
self.mon = mon
def get_step(self):
step = dict()
for it in re.finditer( "(([a-z])(\^([1-9]))?)", self.mon):
print(it.group(), it.group(2), it.group(4))
step[it.group(2)] = 1 if it.group(4) is None else it.group(4)
return step
step1 = Monom(mon0).get_step()
step1
x x None y y None z z None
{'x': 1, 'y': 1, 'z': 1}
step2 = Monom(mon1).get_step()
step2
y y None z z None
{'y': 1, 'z': 1}
symbs1 = set(step1.keys())
symbs1
{'x', 'y', 'z'}
symbs2 = set(step2.keys())
symbs2
{'y', 'z'}
symbs = symbs1 | symbs2
symbs
{'x', 'y', 'z'}
symbs = list( symbs)
symbs
['z', 'x', 'y']
symbs.sort()
symbs
['x', 'y', 'z']
# gt
cmp = 0
for s in symbs:
print(s)
if s in symbs1 and s not in symbs2:
cmp = True
break
if s not in symbs1 and s in symbs2:
cmp = False
break
# В обоих мономах
assert( s in symbs1 and s in symbs2 )
if step1[s] == step2[s]:
continue
cmp = step1[s] > step2[s]
x
cmp
True
class Monom:
def __init__(self, mon):
self.mon = mon
def get_step(self):
step = dict()
for it in re.finditer( "(([a-z])(\^([1-9]))?)", self.mon):
#print(it.group(), it.group(2), it.group(4))
step[it.group(2)] = 1 if it.group(4) is None else int(it.group(4))
print(step)
return step
def __gt__(self, other):
print('gt')
mystep = self.get_step()
#print('--')
otstep = other.get_step()
msymbs = set(mystep.keys())
osymbs = set(otstep.keys())
asymbs = list(msymbs | osymbs)
asymbs.sort()
for s in asymbs:
print(s)
if s in msymbs and s not in osymbs:
return True
if s not in msymbs and s in osymbs:
return False
# В обоих мономах
assert( s in msymbs and s in osymbs )
if mystep[s] == otstep[s]:
continue
return mystep[s] > otstep[s]
return False
monom0 = Monom(mon0)
monom1 = Monom(mon1)
monom0>monom1
gt {'x': 1, 'y': 1, 'z': 1} {'y': 1, 'z': 1} x
True
Monom(mon2) > Monom(mon3)
gt {'x': 2, 'y': 1, 'z': 1} {'x': 1, 'y': 3, 'z': 1} x
True
Monom(mon0) > Monom(mon3)
gt {'x': 1, 'y': 1, 'z': 1} {'x': 1, 'y': 3, 'z': 1} x y
False
Сработает и
Monom(mon0) < Monom(mon3)
gt {'x': 1, 'y': 3, 'z': 1} {'x': 1, 'y': 1, 'z': 1} x y
True
Monom(mon0)
<__main__.Monom at 0x7fc9ed7429e8>
Monom(mon0) == Monom(mon0)
False
Что то не то выдал
class Monom:
def __init__(self, mon):
self.mon = mon
def get_step(self):
step = dict()
for it in re.finditer( "(([a-z])(\^([1-9]))?)", self.mon):
#print(it.group(), it.group(2), it.group(4))
step[it.group(2)] = 1 if it.group(4) is None else int(it.group(4))
print(step)
return step
def __gt__(self, other):
print('gt')
mystep = self.get_step()
#print('--')
otstep = other.get_step()
msymbs = set(mystep.keys())
osymbs = set(otstep.keys())
asymbs = list(msymbs | osymbs)
asymbs.sort()
for s in asymbs:
print(s)
if s in msymbs and s not in osymbs:
return True
if s not in msymbs and s in osymbs:
return False
# В обоих мономах
assert( s in msymbs and s in osymbs )
if mystep[s] == otstep[s]:
continue
return mystep[s] > otstep[s]
return False
def __eq__(self, other):
print('eq')
mystep = self.get_step()
#print('--')
otstep = other.get_step()
return mystep == otstep
Monom(mon0) == Monom(mon0)
eq {'x': 1, 'y': 1, 'z': 1} {'x': 1, 'y': 1, 'z': 1}
True
Но не сработают другие сравнения:
Monom(mon0) >= Monom(mon0)
--------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-146-3349fe288756> in <module> ----> 1 Monom(mon0) >= Monom(mon0) TypeError: '>=' not supported between instances of 'Monom' and 'Monom'
import functools as ft
@ft.total_ordering
class Monom:
def __init__(self, mon):
self.mon = mon
def get_step(self):
step = dict()
for it in re.finditer( "(([a-z])(\^([1-9]))?)", self.mon):
#print(it.group(), it.group(2), it.group(4))
step[it.group(2)] = 1 if it.group(4) is None else int(it.group(4))
print(step)
return step
def __gt__(self, other):
print('gt')
mystep = self.get_step()
#print('--')
otstep = other.get_step()
msymbs = set(mystep.keys())
osymbs = set(otstep.keys())
asymbs = list(msymbs | osymbs)
asymbs.sort()
for s in asymbs:
print(s)
if s in msymbs and s not in osymbs:
return True
if s not in msymbs and s in osymbs:
print('False')
return False
# В обоих мономах
assert( s in msymbs and s in osymbs )
if mystep[s] == otstep[s]:
continue
if mystep[s] > otstep[s]:
return True
else:
break
print('False')
return False
def __eq__(self, other):
print('eq')
mystep = self.get_step()
#print('--')
otstep = other.get_step()
return mystep == otstep
Monom(mon0) >= Monom(mon0)
gt {'x': 1, 'y': 1, 'z': 1} {'x': 1, 'y': 1, 'z': 1} x y z False eq {'x': 1, 'y': 1, 'z': 1} {'x': 1, 'y': 1, 'z': 1}
True
Monom(mon2) >= Monom(mon3)
gt {'x': 2, 'y': 1, 'z': 1} {'x': 1, 'y': 3, 'z': 1} x
True
class Vec3d:
def __init__(self, x, y, z):
self.vec = np.array([x, y, z])
def add(self, b):
v = self.vec + b.vec
return Vec3d( *v )
def __format__(self, format):
return f'({self.vec[0]}, {self.vec[1]}, {self.vec[2]})'
a = Vec3d(2, 3, 1)
b = Vec3d(-1, 2, -1)
c = a.add(b)
c.vec
array([1, 5, 0])
print( f"{a}" )
(2, 3, 1)
class Vec3d:
def __init__(self, x, y, z):
self.vec = np.array([x, y, z])
def __add__(self, b):
v = self.vec + b.vec
return Vec3d( *v )
a = Vec3d(2, 3, 1)
b = Vec3d(-1, 2, -1)
c = a+b
c.vec
array([1, 5, 0])
a+1
--------------------------------------------------------------------------- AttributeError Traceback (most recent call last) <ipython-input-158-98b939904c8e> in <module> ----> 1 a+1 <ipython-input-155-80579fe9bdcf> in __add__(self, b) 3 self.vec = np.array([x, y, z]) 4 def __add__(self, b): ----> 5 v = self.vec + b.vec 6 return Vec3d( *v ) AttributeError: 'int' object has no attribute 'vec'
isinstance(a, Vec3d)
True
class Vec3d:
def __init__(self, x, y, z):
self.vec = np.array([x, y, z])
def __add__(self, b):
if isinstance(b, Vec3d):
c = b.vec
else:
c = np.array([b, b, b])
v = self.vec + c
return Vec3d( *v )
def __format__(self, format):
return f'({self.vec[0]}, {self.vec[1]}, {self.vec[2]})'
a = Vec3d(2, 3, 1)
b = Vec3d(-1, 2, -1)
c = a+b
c.vec
array([1, 5, 0])
d = a+1
print( f"{d}" )
(3, 4, 2)
1+a
--------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-165-014d72a43ff4> in <module> ----> 1 1+a TypeError: unsupported operand type(s) for +: 'int' and 'Vec3d'
class Vec3d:
def __init__(self, x, y, z):
self.vec = np.array([x, y, z])
def __add__(self, b):
if isinstance(b, Vec3d):
c = b.vec
else:
c = np.array([b, b, b])
v = self.vec + c
return Vec3d( *v )
def __radd__(self, b):
if isinstance(b, Vec3d):
c = b.vec
else:
c = np.array([b, b, b])
v = self.vec + c
return Vec3d( *v )
def __format__(self, format):
return f'({self.vec[0]}, {self.vec[1]}, {self.vec[2]})'
a = Vec3d(2, 3, 1)
e = 2+a
print( f"{e}" )
(4, 5, 3)
Можно и так:
b = Vec3d(-1, 2, -1)
b += a
А можно переопределить:
class Vec3d:
def __init__(self, x, y, z):
self.vec = np.array([x, y, z])
def __add__(self, b):
if isinstance(b, Vec3d):
c = b.vec
else:
c = np.array([b, b, b])
v = self.vec + c
return Vec3d( *v )
def __iadd__(self, b):
if isinstance(b, Vec3d):
c = b.vec
else:
c = np.array([b, b, b])
v = self.vec + 2*c
return Vec3d( *v )
def __format__(self, format):
return f'({self.vec[0]}, {self.vec[1]}, {self.vec[2]})'
a = Vec3d(2, 3, 1)
b = Vec3d(-1, 2, -1)
(b+a).vec
array([1, 5, 0])
b += a
b.vec
array([3, 8, 1])
class Vec3d:
def __init__(self, x, y, z):
self.vec = np.array([x, y, z])
def __add__(self, b):
v = self.vec + b.vec
return Vec3d( *v )
def __sub__(self, b):
v = self.vec - b.vec
return Vec3d( *v )
def __mul__(self, b):
return np.sum(self.vec * b.vec)
def __format__(self, format):
return f'({self.vec[0]}, {self.vec[1]}, {self.vec[2]})'
a = Vec3d(2, 3, 1)
b = Vec3d(-1, 2, -1)
a*b
3
class collect( stats ): # Дитя.
def __init__(self):
self.data = []
print('child init')
super( collect, self).__init__()
def add( self, x ):
self.data.append( x )
def calc_stats( self ):
self.calc( self.data )
#super( collect, self).calc( self.data )#Можно и так.
collect.__base__
__main__.stats
acum = collect()
# Обрати внимание на порядок печатания строк.
child init simple call!
acum.__dict__
{'data': [], 'mean': 0, 'std': 0, 'all_means': []}
acum.add( 1. )
acum.add( 2. )
acum.data
[1.0, 2.0]
acum.__dict__
{'data': [1.0, 2.0], 'mean': 0, 'std': 0, 'all_means': []}
acum.calc_stats()
acum.pri()
1.5 0.5
acum.add( -2. )
acum.calc_stats()
acum.pri()
0.3333333333333333 1.699673171197595
Подмена вызова
В других языках такой метод называется виртуальным.
class stats:
all_means = []
def __init__( self, x = None ):
if x is None:
print('simple call!')
self.mean = 0
self.std = 0
return
print('data call!')
self.calc( x )
#def filt( self, x): # Данного метода нет!
# return x
def whoami( self ):
print('stats')
def calc( self, x ):
x = self.filt( x ) # Использует метод filt
self.mean = np.mean( x ) # Мы считаем, что он есть.
self.std = np.std( x )
self.all_means.append( self.mean )
def pri( self ):
print( self.mean, self.std)
z = stats()
simple call!
z.whoami()
stats
z.calc(a)
--------------------------------------------------------------------------- AttributeError Traceback (most recent call last) <ipython-input-187-67d74f17de46> in <module> ----> 1 z.calc(a) <ipython-input-184-a49fb9c1cba6> in calc(self, x) 17 18 def calc( self, x ): ---> 19 x = self.filt( x ) # Использует метод filt 20 self.mean = np.mean( x ) # Мы считаем, что он есть. 21 self.std = np.std( x ) AttributeError: 'stats' object has no attribute 'filt'
class collect( stats ):
def __init__(self):
self.data = []
super( collect, self).__init__()
def filt( self, x): # Новый фильтр. Он подменяет из родительского.
xx = list(filter( lambda x: x>0, x))
return xx
def whoami(self):
print('collect')
super(collect, self).whoami()
def add( self, x ):
self.data.append( x )
def calc_stats( self ):
self.calc( self.data )
collect.__dict__
mappingproxy({'__module__': '__main__', '__init__': <function __main__.collect.__init__(self)>, 'filt': <function __main__.collect.filt(self, x)>, 'whoami': <function __main__.collect.whoami(self)>, 'add': <function __main__.collect.add(self, x)>, 'calc_stats': <function __main__.collect.calc_stats(self)>, '__doc__': None})
acum_pos = collect()
simple call!
acum_pos.add( 1. )
acum_pos.add( 2. )
acum_pos.add( -2. )
# При подсчете статистик будет вызван новый фильтр.
acum_pos.calc_stats()
acum_pos.pri() # Учитывались только положительные!
1.5 0.5
acum_pos.whoami()
collect stats