Классы в Python это полноценные объекты, даже если нет ни одного экземпляра.
Самый простой класс создается так:
>>> class database :
pass
>>>
Это пустой класс с именем database без атрибутов и без методов.
Создадим класс с одним атрибутом:
>>> class database :
name = 'oracle'
>>>
Теперь мы можем обращаться к этому атрибуту класса за пределами самого класса:
>>> print(database.name)
oracle
>>>
и даже изменять его значение:
>>> database.name = "ora11gR2"
>>> print(database.name)
ora11gR2
>>>
Тоже самое можно сделать и так:
- сначала создаем пустой класс
>>> class database :
pass
>>>
После создания класса мы можем присоединить к нему атрибуты,
выполняя операции присваивания за пределами класса:
>>> database.name = "oracle"
>>> database.version = "11.2.0.2"
>>>
>>> print(database.name)
oracle
>>> print(database.version)
11.2.0.2
>>>
>>> database.version = "11.2.0.3"
>>> print(database.version)
11.2.0.3
>>>
Пока у нас имеется ссылка на класс, мы можем в любое время
добавлять или изменять его атрибуты по своему усмотрению.
Пусть у нас имеется простой класс с одним атрибутом:
>>> class test:
name = 'Larry'
>>>
Создадим два экземпляра этого класса:
>>> x = test()
>>> y = test()
>>>
так как они помнят класс из которого были созданы,
они по наследству получат атрибуты класса:
>>> x.name, y.name
('Larry', 'Larry')
>>>
Если выполнить присваивание атрибуту экземпляра,
то будет создан (или изменен) атрибут этого конкретного объекта, а не другого.
Атрибуты обнаруживаются в результате поиска по дереву наследования,
но операция присваивания значения атрибуту воздействует только на тот объект,
к которому эта операция применяется.
>>>
>>> x.name = "Tom"
>>>
>>> test.name, x.name, y.name
('Larry', 'Tom', 'Larry')
>>>
Экземпляр x получил свой собственный атрибут name,
а экземпляр y по прежнему наследует атрибут name, присоединенный к классу test.
Добавим в наш простой класс метод setname с помощью которого мы получим возможность
изменять значение атрибута.
>>> class test:
name = 'Larry'
def setname(self, value):
self.name = value
>>>
Создадим экземпляр класса test.
>>> x = test()
>>>
Он также по наследству получит атрибут name.
>>> x.name
'Larry'
>>>
Но теперь мы можем вызвать метод класса setname который изменяет значение атрибута name.
В качестве параметра укажем, атрибут какого экземпляра мы хотим изменить а также новое значение атрибута name.
>>> test.setname(x, "Tom")
>>>
Мы вызвали данный метод, как метод класса test.
test.setname(...)
Но так как наш экземпляр x, также по наследству от класса test получит и метод setname:
def setname(self, value):
self.name = value
но уже в таком виде:
def setname(x, value):
x.name = value
тут уже первый параметр - это имя созданного экземпляра.
То теперь проще вызвать данный метод не из класса test, а из созданного экземпляра x:
>>> x.setname("Tom")
>>>
Имя self внутри метода автоматически ссылается на обрабатываемый экземпляр x,
поэтому операция присваивания сохраняет значение в пространстве имен экземпляра а не класса.
В классе test атрибуты можно и не объявлять, а оставить только метод setname.
>>> class test:
def setname(self, value):
self.name = value
>>>
Тогда, после создания экземпляра
>>> x = test()
>>>
Никаких атрибутов он от класса не унаследует.
Унаследуется только метод setname.
И только после первого вызова метода setname
>>> x.setname("Larry")
>>>
У нас появится новый атрибут экземпляра с именем name
>>> x.name
'Larry'
>>>
Методы, которые обычно создаются инструкциями def, вложенными в инструкцию class,
могут создаваться совершенно независимо от объекта класса.
Пусть у нас имеется класс:
>>> class test:
name = 'Larry'
def setname(self, value):
self.name = value
>>>
Мы хотим добавить в этот класс еще один метод getname,
который выводит текущее значение атрибута name.
Определим некую функцию вне класа:
def xyz(self):
print(self.name)
Это обычная функция, она ничего незнает о классе test.
Она может вызываться как обычная функция.
Есть только единственное ограничение:
Объект, который она получает в качестве параметра, должен иметь атрибут name.
(Имя аргумента "self" не имеет никакого особого смысла и может называться как угодно)
Вызовем эту функцию, передав ей в качестве параметра наш ранее созданный объект x.
>>> xyz(x)
Larry
>>>
Однако, если эту функцию присвоить атрибуту нашего класса, она станет методом, вызываемым из любого экземпляра.
(А также через имя самого класса при условии, что функции вручную будет передан экземпляр)
>>> test.getname = xyz
>>> x.getname()
Larry
Вызвать через имя класса можно так:
>>> test.getname(x)
Larry
>>>
В итоге у нас получился такой класс:
class test:
name = 'Larry'
def setname(self, value):
self.name = value
def getname(self):
print(self.name)
Определим новый класс testnew, который наследует все имена из класса test и добавляет свои собственные.
>>> class testnew(test):
def dispname(self):
print('Current value = "%s"' % self.name)
# и такой же метод как и в test
def getname(self):
print('Value = "%s"' % self.name)
>>>
>>> x = test()
>>> z = testnew()
>>> z.setname("Tom")
>>> z.getname() # вызовется метод из класса testnew
Value = "Tom"
>>> x.getname() # вызовется метод из класса test
Larry
>>>
Допустим что класс test находится в модуле modtest.py
Мы хотим создать класс testnew который бы унаследовал все имена класса test.
Тогда нужно импортировать этот модуль
from modtest import test
class testnew(test):
...
Или эквивалентный вариант, импортируем весь модуль целиком:
import modtest
class testnew(modtest.test):
...
тут в скобках мы указываем полное имя.
Пусть имеется файл modtest.py и в нем определен класс:
class test:
Чтобы получить доступ к этому классу, нам необходимо обратиться к модулю, как обычно:
import modtest
x = modtest.test()
обращаемся к модулю и классу внутри модуля.
Можно также использовать инструкцию from
from modtest import test
x = test()
тут обращаемся только к классу.
Согласно общепринятым соглашениям, имена классов в языке Python должны начинаться с заглавной буквы,
а имена модулей с прописных.
import modtest
x = modtest.Test()
Пусть имеется класс:
class MyClass:
def display(self):
print('Current value = "%s"' % self.data)
Создадим такой класс:
>>> class MyNewClass(MyClass):
def __init__(self, value):
self.data = value
def __add__(self, other):
return MyNewClass(self.data + other)
def __str__(self):
return 'MyNewClass: ' + self.data
def mul(self, other):
self.data *= other
>>> a = MyNewClass("abc")
>>> a.display()
Current value = "abc"
>>> print(a)
MyNewClass: abc
>>> b = a + 'xyz'
>>> b.display()
Current value = "abcxyz"
>>> print(b)
MyNewClass: abcxyz
>>> a.mul(3)
>>> print(a)
MyNewClass: abcabcabc
Обратите внимание, что метод __add__ создает и возвращает новый объект экземпляра
этого класса (вызывая MyNewClass, которому передается значение результата)
А метод mul изменяет текущий объект экземпляра выполняя присваивание атрибуту аргумента self.
Обычно встроенные типы, такие как числа и строки, всегда создают новые объекты при выполнении оператора *.
Атрибуты пространства имен.
>>> class test:
pass
>>>
>>> test.name = "Tom"
>>> test.age = 40
>>>
>>> x = test()
>>> y = test()
>>>
>>> print(x.name) # Унаследованные атрибуты
Tom
>>> print(y.name) # Унаследованные атрибуты
Tom
>>>
>>> x.name = "Larry"
>>> print(x.name) # Экземпляр x получил собственный атрибут
Larry
>>>
Атрибуты пространства имен обычно реализованы в виде словарей.
И деревья наследования классов тоже всего лишь словари со ссылками на другие словари.
Например в большинстве объектов, созданных на базе классов имеется атрибут :
__dict__
Который является словарем пространства имен.
>>> test.__dict__.keys()
dict_keys(['__module__', 'name', 'age', '__dict__', '__weakref__', '__doc__'])
>>>
>>> list(x.__dict__.keys())
['name']
>>>
>>> list(y.__dict__.keys())
[]
>>>
Как видим в словаре класса присутствуют атрибуты name и age, которые созданы ранее.
Объект x имеет свой собственный атрибут name, а объект y по прежнему пуст.
Каждый экземпляр имеет ссылку на свой наследуемый класс, она называется :
__class__
>>> x.__class__
>>>
Классы также имеют атрибут __bases__, который представляет собой картеж его суперклассов:
>>> test.__bases__
(
>>>
Эти два атрибута описывают, как деревья классов размещаются в памяти.
Классы и экземпляры - это всего лишь объекты пространства имен
с атрибутами создаваемыми на лету с помощью операции присваивания.
Обычно эти операции присваивания выполняются внутри инструкции class,
но они могут находиться в любом другом месте, где имеется ссылка на один из объектов в дереве.
#Модуль: test1.py
gl1 = 999 # Глобальная переменная модуля
def fn1(): # Имя функции глобально в пределах модуля
lf1 = 111 # Локальная переменная в функции
print("-fn1-")
print("Видна только в функции", lf1)
class Cl1:
ac1 = 888 # Атрибут класса
def mt1(self): # Метод экземпляра
lm1 = 777 # Локальная переменная в методе
self.ae1 = 555 # Атрибут экземпляра
print("-mt1-")
print("Видна только в методе", lm1)
print("Видна только в экземпляре", self.ae1)
def mt2(): # Статический метод
lm2 = 333 # Локальная переменная в методе
print("-mt2-")
print("Видна только в методе", lm2)
mt2 = staticmethod(mt2)
def mt3(cls): # Метод класса
lm3 = 444 # Локальная переменная в методе
print("-mt2-")
print("Видна только в методе", lm3)
mt3 = classmethod(mt3)
# Глобальная переменная модуля
print(gl1)
# Функция модуля
fn1()
# Атрибут класса
print(Cl1.ac1)
# Обращение к методам экземпляра
em1 = Cl1() # Создаем ссылку экземпляр класса
Cl1.mt1(em1) # Обязательно передаем имя экземпляра первым параметром
em1.mt1() # Или так
Cl1().mt1() # Ссылку на экземпляр можно и не создавать
# Обращение к статическим методам
Cl1.mt2() # имя экземпляра первым параметром не передается
em1.mt2() # имя экземпляра первым параметром не передается
# Обращение к методам класса
Cl1.mt3() # автоматически передается имя класса первым параметром
em1.mt3() # автоматически передается имя класса первым параметром
#Модуль: test2.py
Подсчет количества экземпляров класса:
class Cl1:
ac1 = 0 # Атрибут класса
def __init__(self):
self.ae1 = 555 # Атрибут экземпляра
print("-init-")
Cl1.ac1 +=1
def __del__(self):
print("-del-")
Cl1.ac1 -=1
def mt1(self): # Метод экземпляра
print(Cl1.ac1)
ex1 = Cl1()
ex1.mt1()
ex2 = Cl1()
ex3 = Cl1()
ex1.mt1()
del ex2
del ex3
ex1.mt1()