Классы обь являются при помощи ключ слова class название класса должно начинаться с большой буквы.
class Sample(object):
pass
print(x)
print(type(x))
# Вывод
# <__main__.Sample object at 0x7f8d2b1b0518>
# <class '__main__.Sample'>
При выводе класса можно увидеть его место положение в памяти компьютера.
Мы можем сделать с классом всего 2 вещи, добавить Атрибуты и методы, атрибуты это значения, переменные которые принадлежат классу, а методы это действия которые может производить этот класс.
Атрибуты создаются при помощи ключ слова self
так образ можно
обьявить атрибут или поле, которое будет принадлежать объекту класса.
self.atribute = something
Однако есть одна особенность конкретно языка питон, если в php
мы могли обьявлять статические и динамические переменные вне какого
либо метода, просто описывая его в классе.
В питоне требуется чтобы каждый динамический атрибут был определен в
методе конструкторе __init__()
и только там.
Методы создаются так же как и обычные функции, но в качестве первого
аргумента должен идти аргумент self
В качестве первого аргумента может выступать любое название, но для
удобства используется именно название self
и только оно.
Помимо атрибутов оббита, которые в других языках называются динамическими полями, также в питоне существуют атрибуты класса, которые в других языках называются статическими полями.
Форма записи атрибутов класса, такова:
class MyClass():
static_atribute = "Это атрибут класса, он статичен"
def __init__(self):
print(MyClass.static_atribute)
print(MyClass.static_atribute)
x = MyClass()
Интересен тот факт, что к атрибуту класса можно обращаться при помощи
не только способа MyClass.static_atribute
но и через его объект, к
примеру так:
class MyClass():
static_atribute = "Атрибут класса"
def __init__(self):
self.name = "атрибут обьекта"
x = MyClass
print(x.static_atribute)
print(MyClass.static_atribute)
# Вывод
# Атрибут класса
# Атрибут класса
Таким образом мы можем иметь доступ к атрибутам класса, как через сам класс, так и через его обьект.
У каждого объекта есть спец поле __dict__
в котором содержится
таблица символов объекта, или просто его переменные.
Существует 2 атрибута __dict__
для объектов и для самого класса,
есть такой класс:
class My_Class():
static_var = 'Это статичная переменная класса'
def __init__(self, name, my_list)
self.name = name
self.my_list = my_list
В этом классе мы определили 2 переменные объекта и одну переменную
класса, атрибут __dict__
для объекта будет иметь 2 переменные
которые определены для объекта, то время как переменная класса
будет доступна для __dict__
если мы вызываем его у класса.
Вот как это выглядит:
var1 = My_Class('Название Первое', [1, 2, 3, 4, 5])
var2 = My_Class('Название Второе', ['one', 'two'])
print(var1.__dict__)
print(var2.__dict__)
print(My_Class.__dict__)
# Вывод
# var1.__dict__
# {'name': 'Название Первое', 'my_list': [1, 2, 3, 4, 5]}
# var2.__dict__
# {'name': 'Название Второе', 'my_list': ['one', 'two']}
# My_Class.__dict__
# {
# '__module__': '__main__',
# 'static_var': 'Это статичная переменная класса',
# '__init__': <function func8.<locals>.My_Class.__init__ at 0x7f96168df0d0>,
# '__dict__': <attribute '__dict__' of 'My_Class' objects>,
# '__weakref__': <attribute '__weakref__' of 'My_Class' objects>,
# '__doc__': None
# }
Так видим что когда мы обращаемся к объекту var2.__dict__
то и
получаем переменные объекта, обращаясь к классу My_Class.__dict__
получаем все переменные класса.
В данном примере мы обращались на прямую к __dict__
но также функция
vars()
при получении аргумента в виде объекта, тоже обращается к
атрибуту __dict__
этого объекта.
Два следующих примера полностью равнозначны:
print(vars(var1))
print(vars(var2))
print(vars(My_Class))
print(var1.__dict__)
print(var2.__dict__)
print(My_Class.__dict__)
Можно оперировать 2 сущностями, объектами и классами, мы можем создать объект класса, для создания объекта класса, записываем название класса и круглые скобки, что и означает создание объекта:
class Point():
def __init__(self, name):
self.name = name
obj = Point('Название')
print(obj)
print(type(obj))
Сделав вывод значения и типа, увидим местоположение объекта в памяти и тип объекта, то есть класс от которого он создан.
Значение: <__main__.func5.<locals>.Point object at 0x7f09ab561e80>
Тип: <class '__main__.func5.<locals>.Point'>
Создав объект мы можем оперировать его статическими и динамическими атрибутами.
Также помимо создания объекта, мы можем оперировать самим классом, для этого берем сам класс, для этого записываем название самого класса:
class Point():
def __init__(self, name):
self.name = name
print(Point)
print(type(Point))
Как виденном не требуется создавать объект класса, мы можем сразу к
нему обращаться по его имени, только теперь значением класса будет
его местоположение, а его типом будет спец тип данных type
Значение: <class '__main__.func5.<locals>.Point'>
Тип: <class 'type'>
Стандартные модификаторы доступа работают так же как и в других
языках программирования, public
дает доступ всем, protected
дает
доступ в классе и его дочерних классах, но не из вне, private
дает
доступ только в самом классе и больше нигде.
В Python эти модификаторы указываются при помощи нижних подчеркиваний:
- public - любое обычное написание переменной
atribut
- protected
_
одно нижнее подчеркивание_atribut
- private -
__
два нижних подчеркивания__atribut
Статические переменные класса можно указывать в самом классе,
а не в методе инициализаторе __init__(self)
для вызова этой
статической переменной класса, требуется указать название самого
класса и обратится к переменной, делается это так:
class My_class():
"""Это статическая переменная класса"""
static_var = "Статическая переменная класса"
def __init__(self, x, y):
"""Это динамические переменные обьекта"""
self.x = x
self.y = y
def show(self):
print("Динамические:", self.x, self.y)
print("Статические:", My_class.static_var)
print("Статические:", My_class.static_var)
obj = My_class(10, 20)
obj.show()
# Вывод
# Статические: Статическая переменная класса
# Динамические: 10 20
# Статические: Статическая переменная класса
Мы можем обращаться к статическим переменным как внутри класса так и снаружи, также все статически переменные поддерживают модификаторы доступа.
Вот тут очень интересно, обычно требуется создать метод и дать
ему декоратор @staticmethod
также такие методы не требуют
получения self
как первый параметр.
Однако любой метод без первого аргумента self
будет вести себя
как статический метод, даже если он не обернут @staticmethod
По идеи любой метод обь явленный в классе, ведет себя как статический,
То етсь будут работать оба варианта, что через метод без self
и без
@staticmethod
что без self
но с @staticmethod
к примеру так:
class Point():
static_count = 10
def __init__(self, x, y):
self.x = x
self.y = y
def setCoords(self, x, y):
self.x = x
self.y = y
def getCoords(self):
print(f"getCoords {self.x}, {self.y}")
@staticmethod
def geCountOne():
print(f"geCountOne = {Point.static_count}")
def geCountTwo():
print(f"geCountTwo = {Point.static_count}")
pt = Point(1, 2)
pt.getCoords()
pt.setCoords(10, 20)
pt.getCoords()
Point.geCountOne()
Point.geCountTwo()
# Вывод
# getCoords 1, 2
# getCoords 10, 20
# geCountOne = 10
# geCountTwo = 10
Основа механизма инкапсуляции это ограничение доступа к методам и полям объекта и класса.
Для соблюдения правил инкапсуляции переменные обьявляются с указанием доступа, то есть защищенные или приватные, но в таком случае мы не имеем возможности установить их значения.
Для этого используются геттеры и сеттеры, публичные методы для установки значений переменным:
class Point():
def __init__(self):
print("Класс создан")
def setCoords(self, x, y):
self.__x = x
self.__y = y
def getCoords(self):
return self.__x, self.__y
x = Point()
x.setCoords(10, 20)
print(x.getCoords())
Таким образом через промежуточные методы мы можем установить данные даже в закрытые переменные, также в сеттеры можно устанавливать проверку на то какие именно данные будут внесены в закрытые переменные.
К примеру такая проверка:
def setCoords(self, x, y):
if (isinstance(x, int) or isinstance(x, float)) and \
(isinstance(y, int) or isinstance(y, float)) :
self.__x = x
self.__y = y else:
print("Координаты должны быть числами")
При такой проверке мы не сможем вставить в переменную неправильные данные, также можно вынести валидацию данных в отдельный метод, и проверять их так:
class Point():
def __init__(self):
print("Класс Point создан")
def __checkValue(x):
if isinstance(x, int) or isinstance(x, float):
return True
return False
def setCoords(self, x, y):
if __checkValue(x) and __checkValue(y):
self.__x = x
self.__y = y
else:
print("Координаты должны быть числами")
def getCoords(self):
return self.__x, self.__y