Основное наследование
Наследование в Python основано на сходных идеях, используемых в других объектно-ориентированных языках, таких как Java, C ++ и т. Д. Новый класс может быть получен из существующего класса следующим образом.
class BaseClass(object):
pass
class DerivedClass(BaseClass):
pass
BaseClass
является уже существующий (родительский) класс, а DerivedClass
это новый (дочерний) класс , который наследует (или подклассов) атрибуты BaseClass
.Примечание: По состоянию на Python 2.2, все классы неявно наследуются от object
класса , который является базовым классом для всех встроенных типов.
Определим родительский Rectangle
класс в примере ниже, который неявно наследует от object
:
class Rectangle():
def __init__(self, w, h):
self.w = w
self.h = h
def area(self):
return self.w * self.h
def perimeter(self):
return 2 * (self.w + self.h)
Rectangle
класс может быть использован в качестве базового класса для определения Square
класса, как квадрат является частным случаем прямоугольника.
class Square(Rectangle):
def __init__(self, s):
# вызов конструктора родителя, w и h оба s
super(Square, self).__init__(s, s)
self.s = s
Square
класс автоматически наследует все атрибуты Rectangle
класса, а также класса объектов. super()
используется для вызова __init__()
метод Rectangle
класса, по существу , вызовом любой перекрытый метод базового класса. Примечание: в Python 3, super()
не требует аргументов.
Объекты производного класса могут получать доступ и изменять атрибуты своих базовых классов:
Встроенные функции, которые работают с наследованием
issubclass(DerivedClass, BaseClass)
: возвращает True
, если DerivedClass
является подклассом BaseClass
isinstance(s, Class)
: возвращает True
, если ы является экземпляром Class
или любой из производных классов Class
# subclass check
issubclass(Square, Rectangle)
# True
# instantiate
r = Rectangle(3, 4)
s = Square(2)
isinstance(r, Rectangle)
# True
isinstance(r, Square)
# False
# Прямоугольник это не квадрат
isinstance(s, Rectangle)
# True
# Квадрат это прямоугольник
isinstance(s, Square)
# True
Переменные класса и экземпляра
Переменные экземпляра уникальны для каждого экземпляра, а переменные класса являются общими для всех экземпляров.
class C:
x = 2 # class variable
def __init__(self, y):
self.y = y # instance variable
C.x
# 2
C.y
# AttributeError: type object 'C' has no attribute 'y'
c1 = C(3)
c1.x
# 2
c1.y
# 3
c2 = C(4)
c2.x
# 2
c2.y
# 4
Переменные класса могут быть доступны в экземплярах этого класса, но присвоение атрибуту класса создаст переменную экземпляра, которая затеняет переменную класса
c2.x = 4
c2.x
# 4
C.x
# 2
Обратите внимание , что мутирует переменный класс случаев может привести к неожиданным последствиям.
class D:
x = []
def __init__(self, item):
self.x.append(item)
d1 = D(1)
d2 = D(2)
d1.x
# [1, 2]
d2.x
# [1, 2]
D.x
# [1, 2]
Связанные, несвязанные и статические методы
Идея связанных и несвязанных методов был удален в Python 3.В Python 3 при объявлении метода в классе, вы используете def
ключевое слово, тем самым создавая объект функции. Это обычная функция, и окружающий класс работает как пространство имен. В следующем примере мы указываем метод f
в пределах класса A
, и это становится функцией A.f
:
class A(object):
def f(self, x):
return 2 * x
A.f
В Python 2 поведение отличается: объекты функций внутри класса были неявно заменены объектами типа instancemethod
, которые назывались несвязанных метода , потому что они не были связаны с каким - либо конкретным экземпляром класса. Удалось получить доступ к основной функции с помощью .__func__
свойства.
A.f
# <unbound method A.f> (в Python 2.x)
A.f.__class__
# <type 'instancemethod'>
A.f.__func__
# <function f at ...>
Последнее поведение подтверждается проверкой - методы распознаются как функции в Python 3, в то время как различие поддерживается в Python 2.
Python 3.x /2.3x
import inspect
inspect.isfunction(A.f)
# True
inspect.ismethod(A.f)
# False
В обеих версиях функции Python / метод A.f
может быть вызван непосредственно, при условии , что вы передаете экземпляр класса A
в качестве первого аргумента.
A.f(1, 7)
# Python 2: TypeError: unbound method f() must be called with
# A instance as first argument (got int instance instead)
# Python 3: 14
a = A()
A.f(a, 20)
# Python 2 & 3: 40
A.f(1, 7)
# Python 2: TypeError: unbound method f() must be called with
# A instance as first argument (got int instance instead)
# Python 3: 14
a = A()
A.f(a, 20)
# Python 2 & 3: 40
Теперь предположим , что a
является экземпляром класса A
, что a.f
тогда? Ну, интуитивно это должно быть тем же самым методом f
класса A
, только он должен каким - то образом «знает» , что он был применен к объекту a
- в Python это называется метод , связанный с.a
В суровых буднях детали следующим образом : запись af
вызывает магический __getattribute__
метод , который сначала проверяет , является ли имеет атрибут с именем a
a
f
(не), а затем проверяет , класс A
, содержит ли это метод с таким именем (он делает), и создает новый объект m
типа method
, который имеет ссылку на исходные Af
в m.__func__
и ссылку на объект a
в m.__self__
.Когда этот объект вызывается как функция, он просто выполняет следующие действия : m(...) => m.__func__(m.__self__, ...)
.Таким образом , этот объект называется связанный метод потому , что при вызове он знает , чтобы поставить объект был привязан к качестве первого аргумента. (Эти вещи работают одинаково в Python 2 и 3).
a = A()
a.f
# <bound method A.f of <__main__.A object at ...>>
a.f(2)
# 4
# Note: связаванный метод объекта a.f создается каждый раз когда он вызывается:
a.f is a.f # False
# As a performance optimization you can store the bound method in the object's
# __dict__, in which case the method object will remain fixed:
a.f = a.f
a.f is a.f # True
Наконец, Python имеет методы класса и статические методы - специальные виды методов. Методы класса работают точно так же , как и обычные методы, за исключением того, что при вызове на объекте они связываются с классом объекта , а не к объекту. Таким образом , m.__self__ = type(a)
.При вызове такого связанного метода, он проходит класс в качестве первого аргумента. a
Статические методы еще проще: они вообще ничего не связывают и просто возвращают базовую функцию без каких-либо преобразований.
class D(object):
multiplier = 2
@classmethod
def f(cls, x):
return cls.multiplier * x
@staticmethod
def g(name):
print("Hello, %s" % name)
D.f
# <bound method type.f of <class '__main__.D'>>
D.f(12)
# 24
D.g
# <function D.g at ...>
D.g("world")
# Hello, world
Обратите внимание, что методы класса привязаны к классу даже при обращении к экземпляру:
d = D()
d.multiplier = 1337
(D.multiplier, d.multiplier)
# (2, 1337)
d.f
# <bound method D.f of <class '__main__.D'>>
d.f(10)
# 20
Стоит отметить , что на самом низком уровне, функции, методы, staticmethods и т.д., на самом деле дескрипторы , которые вызывают __get__
, __set
и , возможно , `del__` специальные методы. Для более подробной информации о методах классов и статических методах:
Классы нового стиля против старого стиля
Новые классы в стиле были введены в Python 2.2 для объединения классов и типов. Они наследуют от верхнего уровня object
типа. Класс нового типа является определенный пользователем тип, и очень похож на встроенных типов.
# new-style class
class New(object):
pass
# new-style instance
new = New()
new.__class__
# <class '__main__.New'>
type(new)
# <class '__main__.New'>
issubclass(New, object)
# True
Классы старого типа не наследуют от object
.Экземпляры старого типа всегда реализуется с помощью встроенного в instance
типа.
# old-style class
class Old:
pass
# old-style instance
old = Old()
old.__class__
# <class __main__.Old at ...>
type(old)
# <type 'instance'>
issubclass(Old, object)
# False
В Python 3 классы старого стиля были удалены.
Новые классы стиля в Python 3 неявно наследуют от object
, поэтому нет необходимости указывать MyClass(object)
больше.
class MyClass:
pass
my_inst = MyClass()
type(my_inst)
# <class '__main__.MyClass'>
my_inst.__class__
# <class '__main__.MyClass'>
issubclass(MyClass, object)
# True
Значения по умолчанию для переменных экземпляра
Если переменная содержит значение неизменяемого типа (например, строку), тогда можно назначить значение по умолчанию, подобное этому.
class Rectangle(object):
def __init__(self, width, height, color='blue'):
self.width = width
self.height = height
self.color = color
def area(self):
return self.width * self.height
# Create some instances of the class
default_rectangle = Rectangle(2, 3)
print(default_rectangle.color) # blue
red_rectangle = Rectangle(2, 3, 'red')
print(red_rectangle.color) # red
Нужно быть осторожным при инициализации изменяемых объектов, таких как списки в конструкторе. Рассмотрим следующий пример:
class Rectangle2D(object):
def __init__(self, width, height, pos=[0,0], color='blue'):
self.width = width
self.height = height
self.pos = pos
self.color = color
r1 = Rectangle2D(5,3)
r2 = Rectangle2D(7,8)
r1.pos[0] = 4
r1.pos # [4, 0]
r2.pos # [4, 0] позиция r2's тоже изменилась
Такое поведение вызвано тем, что в Python параметры по умолчанию связаны при выполнении функции, а не при ее объявлении. Чтобы получить переменную экземпляра по умолчанию, которая не разделяется между экземплярами, следует использовать такую конструкцию:
class Rectangle2D(object):
def __init__(self, width, height, pos=None, color='blue'):
self.width = width
self.height = height
self.pos = pos or [0, 0] # default value is [0, 0]
self.color = color
r1 = Rectangle2D(5,3)
r2 = Rectangle2D(7,8)
r1.pos[0] = 4
r1.pos # [4, 0]
r2.pos # [0, 0] позиция r2's не изменилась
Смотрите также Мутабельные Аргументы по умолчанию и «изумление» Наималейшего и изменяемый по умолчанию аргумент .
Множественное наследование
Python использует C3 линеаризацию алгоритм для определения порядка , в котором для решения атрибутов класса, включая методы. Это известно как Порядок разрешения методов (MRO).
Вот простой пример:
class Foo(object):
foo = 'attr foo of Foo'
class Bar(object):
foo = 'attr foo of Bar' # we won't see this.
bar = 'attr bar of Bar'
class FooBar(Foo, Bar):
foobar = 'attr foobar of FooBar'
Теперь, если мы создаем экземпляр FooBar
, если мы ищем атрибут foo
, мы видим, что атрибут Foo
находится первым
fb = FooBar()
fb.foo
#'attr foo of Foo'
Вот MRO FooBar:
FooBar.mro()
#[<class '__main__.FooBar'>, <class '__main__.Foo'>, <class '__main__.Bar'>, <type 'object'>]
Можно просто сказать, что алгоритм Python MRO
- Глубина первого (например ,
FooBar
затемFoo
) , если - общий родительский (
object
) блокируется ребенком (Bar
) и - круговые отношения не допускаются.
То есть, например, Bar не может наследовать от FooBar, а FooBar наследует от Bar.
Другая характерная особенность в наследстве является super
.Супер может получить функции родительских классов.
class Foo(object):
def foo_method(self):
print("foo Method")
class Bar(object):
def bar_method(self):
print("bar Method")
class FooBar(Foo, Bar):
def foo_method(self):
super(FooBar, self).foo_method()
Множественное наследование с помощью метода init класса, когда у каждого класса есть собственный метод init, тогда мы пытаемся получить множественное наследование, тогда вызывается только метод init класса, который наследуется первым.
для примера ниже Foo
метод инициализировать класс вызывался класс Bar
не INIT вызывался
class Foo(object):
def __init__(self):
print("foo init")
class Bar(object):
def __init__(self):
print("bar init")
class FooBar(Foo, Bar):
def __init__(self):
print("foobar init")
super(FooBar, self).__init__()
a = FooBar()
# foobar init
# foo init
Но это не значит, что Bar класс не наследуется. Instance конечного класса FooBar также экземпляр класса Bar и класса Foo.
print(isinstance(a,FooBar))
#True
print(isinstance(a,Foo))
#True
print(isinstance(a,Bar))
#True
Дескрипторы
Дескрипторы являются объектами , которые являются ( как правило) атрибутами классов и которые имеют какие - либо из __get__
, __set__
или __delete__
специальных методов.
Дескрипторы данных имеют какой - либо из __set__
или __delete__
Они могут контролировать пунктирный поиск на экземпляре, и используются для реализации функций, staticmethod
, classmethod
и property
.
Методы класса: альтернативные инициализаторы
Методы класса представляют альтернативные способы создания экземпляров классов. Чтобы проиллюстрировать это, давайте посмотрим на пример.
Давайте предположим , что мы имеем относительно простой Person
класс:
class Person(object):
def __init__(self, first_name, last_name, age):
self.first_name = first_name
self.last_name = last_name
self.age = age
self.full_name = first_name + " " + last_name
def greet(self):
print("Hello, my name is " + self.full_name + ".")
Возможно, было бы удобно иметь возможность создавать экземпляры этого класса, указав полное имя вместо имени и фамилии отдельно. Один из способов сделать это будет иметь last_name
быть необязательным параметром, и при условии , что , если не дано, мы прошли полное имя:
class Person(object):
def __init__(self, first_name, age, last_name=None):
if last_name is None:
self.first_name, self.last_name = first_name.split("", 2)
else:
self.first_name = first_name
self.last_name = last_name
self.full_name = self.first_name + " " + self.last_name
self.age = age
def greet(self):
print("Hello, my name is " + self.full_name + ".")
Однако с этим битом кода связаны две основные проблемы:
- Параметры
first_name
иlast_name
теперь вводит в заблуждение, так как вы можете ввести полное имя дляfirst_name
. - Кроме того, если есть больше падежей и / или больше параметров, которые обладают такой гибкостью, ветвление if / elif / else может быстро раздражать.
Введите методы класса. Вместо того , чтобы иметь один инициализатор, мы создадим отдельный инициализатору, называемый from_full_name
, и украсить его с (встроенный) classmethod
декоратора.
class Person(object):
def __init__(self, first_name, last_name, age):
self.first_name = first_name
self.last_name = last_name
self.age = age
self.full_name = first_name + " " + last_name
@classmethod
def from_full_name(cls, name, age):
if " " not in name:
raise ValueError
first_name, last_name = name.split("", 2)
return cls(first_name, last_name, age)
def greet(self):
print("Hello, my name is " + self.full_name + ".")
Обратите внимание на cls
вместо self
в качестве первого аргумента from_full_name
.Методы класса применяется к общему классу, не является экземпляром данного класса (что self
обычно обозначает). Так что , если cls
является наш Person
класс, то возвращается значение из from_full_name
метода класса является Person(first_name, last_name, age)
, который использует Person
«s __init__
создать экземпляр Person
класса.
В частности, если мы должны были сделать подкласс Employee
из Person
, то from_full_name
будет работать в Employee
классе , а также.
Для того, чтобы показать , что это работает , как и ожидалось, давайте создавать экземпляры Person
в более чем одним способом , без разветвлений в __init__
:
Другие ссылки:
- https://docs.python.org/2/library/functions.html#classmethod
- https://docs.python.org/3.5/library/functions.html#classmethod
Композиция классов
Композиция классов позволяет явные отношения между объектами. В этом примере люди живут в городах, которые принадлежат странам. Композиция позволяет людям получить доступ ко всем людям, живущим в их стране:
class Country(object):
def __init__(self):
self.cities=[]
def addCity(self,city):
self.cities.append(city)
class City(object):
def __init__(self, numPeople):
self.people = []
self.numPeople = numPeople
def addPerson(self, person):
self.people.append(person)
def join_country(self,country):
self.country = country
country.addCity(self)
for i in range(self.numPeople):
person(i).join_city(self)
class Person(object):
def __init__(self, ID):
self.ID=ID
def join_city(self, city):
self.city = city
city.addPerson(self)
def people_in_my_country(self):
x= sum([len(c.people) for c in self.city.country.cities])
return x
US=Country()
NYC=City(10).join_country(US)
SF=City(5).join_country(US)
print(US.cities[0].people[0].people_in_my_country())
# 15
Monkey Patching (исправление обезьяны)
В этом случае «исправление обезьяны» означает добавление новой переменной или метода в класс после его определения. Например, скажем , мы определили класс A
, как
class A(object):
def __init__(self, num):
self.num = num
def __add__(self, other):
return A(self.num + other.num)
Но теперь мы хотим добавить еще одну функцию позже в коде. Предположим, что эта функция выглядит следующим образом.
def get_num(self):
return self.num
Но как же мы добавим это как метод в A
? Это просто мы просто по существу поместить эту функцию в с помощью оператора присваивания. A
A.get_num = get_num
Почему это работает? Потому что функции - это объекты, как и любой другой объект, а методы - это функции, принадлежащие классу.
Функция get_num
должна быть доступна для всех существующих (уже создан) , а также к новым экземплярам A
Эти дополнения доступны для всех экземпляров этого класса (или его подклассов) автоматически. Например:
foo = A(42)
A.get_num = get_num
bar = A(6);
foo.get_num() # 42
bar.get_num() # 6
Обратите внимание, что, в отличие от некоторых других языков, этот метод не работает для определенных встроенных типов и не считается хорошим стилем.
Список всех членов класса
dir()
функция может быть использована для получения списка членов класса, например:
dir(list)
['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']
Обычно ищут только "немагических" участников. Это можно сделать с помощью простого понимания , в котором перечислены члены, имена которых не начиная с __
:
[m for m in dir(list) if not m.startswith('__')]
['append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']
Предостережения:
Классы можно определить __dir__()
метод. Если этот метод существует вызова dir()
будем называть __dir__()
, в противном случае Python будет пытаться создать список членов класса. Это означает, что функция dir может иметь неожиданные результаты. Две цитаты важности из официальной документации питона :
Если объект не содержит каталог ( как правило ), функция пытается все возможное , чтобы собрать информацию из атрибута Dict объекта, если он определен, и от его типа объекта. Полученный список не обязательно является полным, и может быть неточным , если объект имеет собственный GetAttr ().
Примечание: Поскольку реж () поставляется в первую очередь для удобства использования в интерактивной командной строке, он пытается поставить интересный набор имен больше , чем он пытается поставить строго или последовательно определенный набор имен, и его детальное поведение может измениться по релизы. Например, атрибуты метакласса отсутствуют в списке результатов, когда аргумент является классом.
Введение в классы
Класс, функционирующий как шаблон, который определяет основные характеристики конкретного объекта. Вот пример:
class Person(object):
"""A simple class.""" # docstring
species = "Homo Sapiens" # class attribute
def __init__(self, name): # special method
"""This is the initializer. It's a special
method (see below).
"""
self.name = name # instance attribute
def __str__(self): # special method
"""This method is run when Python tries
to cast the object to a string. Return
this string when using print(), etc.
"""
return self.name
def rename(self, renamed): # regular method
"""Reassign and print the name attribute."""
self.name = renamed
print("Now my name is {}".format(self.name))
Есть несколько вещей, на которые стоит обратить внимание при рассмотрении приведенного выше примера.
Теперь давайте сделаем несколько экземпляров нашего Person
класса!
# Instances
kelly = Person("Kelly")
joseph = Person("Joseph")
john_doe = Person("John Doe")
В настоящее время мы имеем три Person
объектов, kelly
, joseph
и john_doe
.
Мы можем получить доступ к атрибутам класса из каждого экземпляра с помощью оператора точки .
Еще раз обратите внимание на разницу между атрибутами класса и экземпляра:
# Attributes
kelly.species
#'Homo Sapiens'
john_doe.species
#'Homo Sapiens'
joseph.species
#'Homo Sapiens'
kelly.name
#'Kelly'
joseph.name
#'Joseph'
Мы можем выполнить методы класса с использованием того же оператора точки .
:
# Methods
john_doe.__str__()
#'John Doe'
print(john_doe)
#'John Doe'
john_doe.rename("John")
#'Now my name is John'
Cвойства
Классы Python поддерживают свойства, которые выглядят как обычный переменный объект, но с возможностью прикрепления пользовательского поведения и документации.
class MyClass(object):
def __init__(self):
self._my_string = ""
@property
def string(self):
return self._my_string
@string.setter
def string(self, new_value):
assert isinstance(new_value, str), \
"Give me a string, not a %r!" % type(new_value)
self._my_string = new_value
@string.deleter
def x(self):
self._my_string = None
Объекта , класса MyClass
, будет иметь имеют свойство .string
, однако его поведение теперь жестко контролируется:
mc = MyClass()
mc.string = "String!"
print(mc.string)
del mc.string
Помимо полезного синтаксиса, описанного выше, синтаксис свойства позволяет проверять или добавлять другие дополнения к этим атрибутам. Это может быть особенно полезно с общедоступными API-интерфейсами, где пользователю должен быть предоставлен определенный уровень помощи.
Другое распространенное использование свойств - это предоставление классу возможности представлять «виртуальные атрибуты» - атрибуты, которые на самом деле не хранятся, но вычисляются только по запросу.
class Character(object):
def __init__(name, max_hp):
self._name = name
self._hp = max_hp
self._max_hp = max_hp
# Make hp read only by not providing a set method
@property
def hp(self):
return self._hp
# Make name read only by not providing a set method
@property
def name(self):
return self.name
def take_damage(self, damage):
self.hp -= damage
self.hp = 0 if self.hp <0 else self.hp
@property
def is_alive(self):
return self.hp != 0
@property
def is_wounded(self):
return self.hp < self.max_hp if self.hp > 0 else False
@property
def is_dead(self):
return not self.is_alive
bilbo = Character('Bilbo Baggins', 100)
bilbo.hp
# out : 100
bilbo.hp = 200
# out : AttributeError: can't set attribute
# hp attribute is read only.
bilbo.is_alive
#True
bilbo.is_wounded
#False
bilbo.is_dead
#False
bilbo.take_damage( 50 )
bilbo.hp
#50
bilbo.is_alive
#True
bilbo.is_wounded
#True
bilbo.is_dead
#False
bilbo.take_damage( 50 )
bilbo.hp
# out : 0
bilbo.is_alive
#False
bilbo.is_wounded
#False
bilbo.is_dead
#True
Синглтон класс
Синглтон - это шаблон, который ограничивает создание экземпляра класса одним экземпляром / объектом. Для получения дополнительной информации о питоных одноэлементных шаблонах проектирования, см здесь .
class Singleton:
def __new__(cls):
try:
it = cls.__it__
except AttributeError:
it = cls.__it__ = object.__new__(cls)
return it
def __repr__(self):
return '<{}>'.format(self.__class__.__name__.upper())
def __eq__(self, other):
return other is self
Другой способ - украсить свой класс. Следуя пример из этого ответа создать класс Singleton:
class Singleton:
# Непотокобезопасный вспомогательный класс для упрощения реализации синглетонов. Это следует использовать в качестве декоратора, а не метакласса, для класса, который должен быть синглтоном.
# Декорированный класс может определить одну функцию `__init__`, которая принимает только аргумент `self`. Кроме этого, нет никаких ограничений, которые применяются к декорированному классу
# Чтобы получить экземпляр синглтона, используйте метод `Instance`. Попытка использовать __call__ приведет к возникновению ошибки TypeError.
# Ограничения: декорированный класс не может быть унаследован.
def __init__(self, decorated):
self._decorated = decorated
def Instance(self):
#Возвращает экземпляр синглтона. При первом вызове он создает новый экземпляр оформленного класса и вызывает его метод __init__. При всех последующих вызовах возвращается уже созданный экземпляр.
try:
return self._instance
except AttributeError:
self._instance = self._decorated()
return self._instance
def __call__(self):
raise TypeError('Singletons must be accessed through `Instance()`.')
def __instancecheck__(self, inst):
return isinstance(inst, self._decorated)
Для использования вы можете использовать Instance
метод
@Singleton
class Single:
def __init__(self):
self.name=None
self.val=0
def getName(self):
print(self.name)
x=Single.Instance()
y=Single.Instance()
x.name='I\'m single'
x.getName() # outputs I'm single
y.getName() # outputs I'm single