Поиск…


Вступление

Python предлагает себя не только как популярный язык сценариев, но также поддерживает парадигму объектно-ориентированного программирования. Классы описывают данные и предоставляют методы для управления этими данными, все они охватываются одним объектом. Кроме того, классы позволяют абстрагироваться путем отделения конкретных деталей реализации от абстрактных представлений данных.

Кодекс, использующий классы, обычно легче читать, понимать и поддерживать.

Основное наследование

Наследование в 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):
        # call parent constructor, w and h are both s
        super(Square, self).__init__(s, s)
        self.s = s

Класс Square автоматически наследует все атрибуты класса Rectangle а также класс объекта. super() используется для вызова метода __init__() класса Rectangle , по существу вызывающего любой переопределенный метод базового класса. Примечание : в Python 3 super() не требует аргументов.

Объекты с производными классами могут получать и изменять атрибуты базовых классов:

r.area()
# Output: 12
r.perimeter()  
# Output: 14

s.area()
# Output: 4
s.perimeter()
# Output: 8

Встроенные функции, которые работают с наследованием

issubclass(DerivedClass, BaseClass) : возвращает True если DerivedClass является подклассом BaseClass

isinstance(s, Class) : возвращает True если s является экземпляром Class или любого из производных классов Class

# subclass check        
issubclass(Square, Rectangle)
# Output: True

# instantiate
r = Rectangle(3, 4)
s = Square(2)

isinstance(r, Rectangle)  
# Output: True
isinstance(r, Square)
# Output: False
# A rectangle is not a square

isinstance(s, Rectangle)
# Output: True
# A square is a rectangle
isinstance(s, Square)
# Output: 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

Доступ к переменным класса можно получить в экземплярах этого класса, но присвоение атрибуту class создаст переменную экземпляра, которая затеняет переменную класса

c2.x = 4
c2.x
# 4
C.x
# 2

Обратите внимание, что мутирующие переменные класса из экземпляров могут привести к некоторым неожиданным последствиям.

class D:
    x = []
    def __init__(self, item):
        self.x.append(item)  # note that this is not an assigment!

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 , и он становится функцией Af :

Python 3.x 3.0
class A(object):
    def f(self, x):
        return 2 * x
A.f
# <function A.f at ...>  (in Python 3.x)

В Python 2 поведение было иным: функциональные объекты внутри класса были неявно заменены объектами типа instancemethod , которые назывались несвязанными методами, потому что они не были привязаны к какому-либо конкретному экземпляру класса. Можно было получить доступ к основной функции, используя свойство .__func__ .

Python 2.x 2.3
A.f
# <unbound method A.f>   (in Python 2.x)
A.f.__class__
# <type 'instancemethod'>
A.f.__func__
# <function f at ...>

Последнее поведение подтверждается проверкой - методы распознаются как функции в Python 3, а различие поддерживается в Python 2.

Python 3.x 3.0
import inspect

inspect.isfunction(A.f)
# True
inspect.ismethod(A.f)
# False
Python 2.x 2.3
import inspect

inspect.isfunction(A.f)
# False
inspect.ismethod(A.f)
# True

В обеих версиях функции / метода Python Af можно вызывать напрямую, при условии, что вы передаете экземпляр класса 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 является экземпляром класса A , что тогда af ? Ну, интуитивно это должен быть тот же метод 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: the bound method object a.f is recreated *every time* you call it:
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__ . Дополнительные сведения о методах класса и staticmethods:

Классы нового стиля и старого стиля

Python 2.x 2.2.0

Классы нового стиля были введены в 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.x 3.0.0

В 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 pos has changed as well

Такое поведение вызвано тем, что параметры 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 pos hasn't changed 

См. Также « Разрешимые аргументы по умолчанию» и «Наименьшее удивление» и параметр Mutable Default Argument .

Многократное наследование

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'>]

Можно просто сказать, что алгоритм MRO Python

  1. Сначала глубина (например, FooBar затем Foo ), если только
  2. общий родитель ( object ) блокируется дочерним элементом ( Bar ) и
  3. не допускаются круговые отношения.

То есть, например, Bar не может наследовать FooBar, а FooBar наследуется от Bar.

Подробный пример в Python см. В записи wikipedia .

Еще одна мощная функция в наследовании - super . 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, мы пытаемся выполнить множественный ineritance, тогда только метод init получает вызов класса, который наследуется первым.

для примера ниже только метод инициализации класса Foo, получивший вызов Bar class 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 не наследуется. Экземпляр конечного класса FooBar также является экземпляром класса Bar и класса Foo .

print isinstance(a,FooBar)
print isinstance(a,Foo)
print isinstance(a,Bar) 

Выход:

True
True
True

Дескрипторы и пунктирные образы

Дескрипторы - это объекты, которые являются (обычно) атрибутами классов и имеют какие-либо из специальных методов __get__ , __set__ или __delete__ .

Дескрипторы данных имеют любой из __set__ или __delete__

Они могут контролировать точечный поиск экземпляра и использоваться для реализации функций, staticmethod , classmethod и property . Точечный поиск (например, экземпляр foo класса Foo поиска атрибута bar то есть foo.bar ) использует следующий алгоритм:

  1. bar просматривается в классе, Foo . Если он есть, и это дескриптор данных , то используется дескриптор данных. Вот как property может контролировать доступ к данным в экземпляре, и экземпляры не могут переопределить это. Если дескриптора данных нет, тогда

  2. bar просматривается в экземпляре __dict__ . Вот почему мы можем переопределять или блокировать методы, вызываемые из экземпляра с точным поиском. Если в экземпляре существует bar , он используется. Если нет, мы тогда

  3. посмотрите в классе Foo для bar . Если это дескриптор , используется протокол дескриптора. Вот как реализованы функции (в этом контексте, несвязанные методы), classmethod и staticmethod . Иначе он просто возвращает объект там, или есть AttributeError

Методы класса: альтернативные инициализаторы

Методы класса представляют альтернативные способы создания экземпляров классов. Чтобы проиллюстрировать это, давайте рассмотрим пример.

Предположим, что у нас относительно простой класс 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 бы в том, чтобы 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 + ".")

Однако в этом бите кода есть две основные проблемы:

  1. Параметры first_name и last_name теперь вводят в заблуждение, так как вы можете ввести полное имя для first_name . Кроме того, если есть больше случаев и / или более параметров, обладающих такой гибкостью, ветвление if / elif / else может раздражать быстро.

  2. Не так важно, но все же стоит указать: что, если last_name - None , но first_name не разбивается на две или более вещи через пробелы? У нас есть еще один уровень проверки ввода и / или обработки исключений ...

Введите методы класса. Вместо того, чтобы иметь один инициализатор, мы создадим отдельный инициализатор, называемый from_full_name , и 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 класса from_full_name - Person(first_name, last_name, age) , которое использует __init__ Person для создания экземпляра класса Person . В частности, если мы должны были создать подкласс Employee of Person , то from_full_name будет работать и в классе Employee .

Чтобы показать, что это работает, как ожидается, давайте создадим экземпляры Person более чем одним способом без ветвления в __init__ :

In [2]: bob = Person("Bob", "Bobberson", 42)

In [3]: alice = Person.from_full_name("Alice Henderson", 31)

In [4]: bob.greet()
Hello, my name is Bob Bobberson.

In [5]: alice.greet()
Hello, my name is Alice Henderson.

Другие ссылки:

Состав класса

Композиция класса допускает явные отношения между объектами. В этом примере люди живут в городах, принадлежащих странам. Композиция позволяет людям получить доступ к числу людей, проживающих в их стране:

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

Патч обезьяны

В этом случае «патч обезьяны» означает добавление новой переменной или метода в класс после того, как он был определен. Например, предположим, что мы определили класс 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(Class)

Например:

>>> 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 может иметь неожиданные результаты. Две цитаты, имеющие важное значение из официальной документации python :

Если объект не предоставляет dir (), функция пытается собрать информацию из атрибута dict объекта, если он определен, и из его объекта типа. Полученный список не обязательно завершен и может быть неточным, если у объекта есть пользовательский getattr ().

Примечание. Поскольку dir () предоставляется в основном как удобство для использования в интерактивном приглашении, он пытается предоставить интересный набор имен больше, чем пытается предоставить строго или последовательно определенный набор имен, а его подробное поведение может релизы. Например, атрибуты metaclass не входят в список результатов, когда аргумент является классом.

Введение в классы

Класс функционирует как шаблон, определяющий основные характеристики конкретного объекта. Вот пример:

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))

При взгляде на приведенный выше пример есть несколько вещей.

  1. Класс состоит из атрибутов (данных) и методов (функций).
  2. Атрибуты и методы просто определяются как обычные переменные и функции.
  3. Как отмечено в соответствующей docstring, метод __init__() называется инициализатором . Это эквивалентно конструктору на других объектно-ориентированных языках, и это метод, который сначала запускается при создании нового объекта или нового экземпляра класса.
  4. Атрибуты, которые относятся ко всему классу, определяются сначала и называются атрибутами класса .
  5. Атрибуты, относящиеся к конкретному экземпляру класса (объекта), называются атрибутами экземпляра . Они обычно определяются внутри __init__() ; это необязательно, но рекомендуется (поскольку атрибуты, определенные за пределами __init__() подвергаются риску доступа к ним до их определения).
  6. Каждый метод, включенный в определение класса, передает этот объект в качестве его первого параметра. Слово « self используется для этого параметра (использование « self на самом деле условно, так как слово « self не имеет неотъемлемого значения в Python, но это одна из самых уважаемых конвенций Python, и вы всегда должны следовать за ней).
  7. Те, которые используются для объектно-ориентированного программирования на других языках, могут быть удивлены несколькими вещами. Во-первых, у Python нет реальной концепции private элементов, поэтому все, по умолчанию, имитирует поведение public ключевого слова C ++ / Java. Для получения дополнительной информации см. Пример «Члены частного класса» на этой странице.
  8. Некоторые из методов класса имеют следующий вид: __functionname__(self, other_stuff) . Все такие методы называются «магическими методами» и являются важной частью классов в Python. Например, перегрузка операторов в Python осуществляется с помощью магических методов. Для получения дополнительной информации см. Соответствующую документацию .

Теперь давайте сделаем несколько экземпляров нашего класса 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'

свойства

Классы Python поддерживают свойства , которые выглядят как обычные объектные переменные, но с возможностью установки пользовательского поведения и документации.

class MyClass(object):

    def __init__(self):
       self._my_string = ""
    
    @property
    def string(self):
        """A profoundly important string."""
        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
# out : True
bilbo.is_wounded
# out : False
bilbo.is_dead
# out : False

bilbo.take_damage( 50 )

bilbo.hp
# out : 50

bilbo.is_alive
# out : True
bilbo.is_wounded
# out : True
bilbo.is_dead
# out : False

bilbo.take_damage( 50 )
bilbo.hp
# out : 0

bilbo.is_alive
# out : False
bilbo.is_wounded
# out : False
bilbo.is_dead
# out : True

Класс Singleton

Синглтон - это шаблон, который ограничивает экземпляр класса одним экземпляром / объектом. Для получения дополнительной информации о шаблонах проектирования синглтона python см. Здесь .

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:
    """
    A non-thread-safe helper class to ease implementing singletons.
    This should be used as a decorator -- not a metaclass -- to the
    class that should be a singleton.

    The decorated class can define one `__init__` function that
    takes only the `self` argument. Other than that, there are
    no restrictions that apply to the decorated class.

    To get the singleton instance, use the `Instance` method. Trying
    to use `__call__` will result in a `TypeError` being raised.

    Limitations: The decorated class cannot be inherited from.

    """

    def __init__(self, decorated):
        self._decorated = decorated

    def Instance(self):
        """
        Returns the singleton instance. Upon its first call, it creates a
        new instance of the decorated class and calls its `__init__` method.
        On all subsequent calls, the already created instance is returned.

        """
        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


Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow