Документация по Python

Классы, объекты и методы в Python

В: Документация по 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):
        # вызов конструктора родителя, 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

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

То есть, например, 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 + ".")

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

  1. Параметры first_name и last_name теперь вводит в заблуждение, так как вы можете ввести полное имя для first_name.
  2. Кроме того, если есть больше падежей и / или больше параметров, которые обладают такой гибкостью, ветвление 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
Еще от кодкамп
Замечательно! Вы успешно подписались.
Добро пожаловать обратно! Вы успешно вошли
Вы успешно подписались на кодкамп.
Срок действия вашей ссылки истек.
Ура! Проверьте свою электронную почту на наличие волшебной ссылки для входа.
Успех! Ваша платежная информация обновлена.
Ваша платежная информация не была обновлена.