Классы и объекты python

Конструктор классов

    #defining constructor  
    def __init__(self, personName, personAge):  
        self.name = personName  
        self.age = personAge

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

Прежде всего, конструктор можно использовать для помещения значений в переменные-члены. Вы также можете распечатать сообщения в конструкторе, чтобы убедиться, что объект был создан.

Метод конструктора начинается с def __init__. После этого первым параметром должно быть значение «self», так как он передает ссылку на экземпляр самого класса. Вы также можете добавить дополнительные параметры, как показано в примере. «personName» и «personAge» – это два параметра, которые необходимо отправить, когда должен быть создан новый объект.

Функциональный стиль в Python

В функциональном программировании вычисления выполняются путем объединения функций, которые принимают аргументы и возвращают конкретное значение (или значения). Эти функции не изменяют свои входные аргументы и не изменяют состояние программы. Они просто предоставляют результат данного вычисления. Такие функции обычно называются чистыми функциями (pure functions).

Теоретически программы, построенные с использованием функционального стиля, проще:

  • Разрабатывать, потому что вы можете кодировать и использовать каждую функцию изолированно
  • Отлаживать и тестировать, потому что вы можете тестировать и отлаживать отдельные функции, не глядя на остальную часть программы
  • Понимать, потому что вам не нужно иметь дело с изменениями состояния на протяжении всей программы

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

  1. Сопоставление (Mapping) заключается в применении функции преобразования к итерируемому объекту для создания нового объекта. Элементы в новой итерации создаются путем вызова функции преобразования для каждого элемента в исходной итерации.
  2. Фильтрация (Filtering) состоит из применения предиката или булевозначной функции (predicate or Boolean-valued function) к итерируемому объекту для создания нового итерируемого объекта. Элементы в новой итерации создаются путем фильтрации любых элементов в исходной итерации, которые заставляют функцию предиката возвращать false.
  3. Сокращение (Reducing) состоит из применения функции reduce к итерируемому объекту для получения единственного накопленного значения.

По словам Гвидо ван Россума, на Python в большей степени влияют императивные языки программирования, чем функциональные языки:

Однако еще в 1993 году сообщество Python требовало некоторых функций функционального программирования. Они просили:

  • Анонимные функции
  • Функцию 
  • Функцию  
  • Функцию  

Эти функциональные возможности были добавлены в язык благодаря участию многих членов сообщества. В настоящее время  ,  и  являются фундаментальными компонентами стиля функционального программирования в Python.

В этом руководстве мы рассмотрим одну из этих функциональных возможностей — встроенную карту функций map(). Вы также узнаете, как использовать составные части списковых включений (comprehensions) и выражения генератора (generator expressions), чтобы получить ту же функциональность, что и map(), в питоническом и удобочитаемом виде.

Inheritance

You can subclass data classes quite freely. As an example, we will extend our example with a field and use it to record capitals:

In this simple example, everything works without a hitch:

>>>

The field of is added after the three original fields in . Things get a little more complicated if any fields in the base class have default values:

This code will immediately crash with a complaining that “non-default argument ‘country’ follows default argument.” The problem is that our new field has no default value, while the and fields have default values. The data class will try to write an method with the following signature:

However, this is not valid Python. . In other words, if a field in a base class has a default value, then all new fields added in a subclass must have default values as well.

Another thing to be aware of is how fields are ordered in a subclass. Starting with the base class, fields are ordered in the order in which they are first defined. If a field is redefined in a subclass, its order does not change. For example, if you define and as follows:

Then the order of the fields in will still be , , , . However, the default value of will be .

>>>

Определение класса

Вот самая базовая структура определения класса Python.

class ClassName:  
    # list of python class variables  
    # python class constructor  
    # python class method definitions

Теперь поработаем с реальными примерами.

#definition of the class starts here  
class Person:  
    #initializing the variables  
    name = ""  
    age = 0  
      
    #defining constructor  
    def __init__(self, personName, personAge):  
        self.name = personName  
        self.age = personAge  
  
    #defining class methods  
    def showName(self):  
        print(self.name)  
  
    def showAge(self):  
        print(self.age)  
          
    #end of the class definition  
  
# Create an object of the class  
person1 = Person("John", 23)  
#Create another object of the same class  
person2 = Person("Anne", 102)  
#call member methods of the objects  
person1.showAge()  
person2.showName() 

Этот пример не требует пояснений. Как мы знаем, строки, начинающиеся с символа «#», представляют собой комментарии в Python. Они объясняют исполняемые шаги, а код дает следующий результат.

Определение класса в Python:

class Person: 

Эта строка отмечает начало определения класса для класса «Person».

Определение класса

Как и определения функций в Python начинаются с ключевого слова def, определения классов начинаются с ключевого слова class.

Первая строка внутри класса называется docstring и содержит краткое описание класса. Хотя это не обязательно, это настоятельно рекомендуется.

Вот простое определение класса:

class MyNewClass:
    '''This is a docstring. I have created a new class'''
    pass

Класс создает новое локальное пространство имен, в котором определены все его атрибуты. Атрибуты могут быть данными или функциями.

В нем также есть специальные атрибуты, которые начинаются с двойного подчеркивания __. Например, __doc__ дает нам строку документации этого класса.

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

class Person:
    "This is a person class"
    age = 10

    def greet(self):
        print('Hello')


# Output: 10
print(Person.age)

# Output: <function Person.greet>
print(Person.greet)

# Output: 'This is my second class'
print(Person.__doc__)

Выход

10
<function Person.greet at 0x7fc78c6e8160>
This is a person class

Множественная логистическая регрессия

Конечно, логистическая регрессия может быть легко расширена для размещения более одного предиктора:

Множественная логистическая регрессия

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

Линейный Дискриминантный Анализ

Теперь мы понимаем, как работает логистическая регрессия, но, как и любая модель, она имеет некоторые недостатки:

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

Вот где линейный дискриминантный анализ (LDA) пригодится. Он более стабилен, чем логистическая регрессия, и широко используется для прогнозирования более двух классов.

Особенность LDA заключается в том, что он моделирует распределение предикторов отдельно в каждом из классов ответов, а затем использует теорему Байеса для оценки вероятности.

Хорошо, это немного сложно понять. Давайте разберемся с этим.

Создание объектов

# Create an object of the class  
person1 = Person("Richard", 23)  
#Create another object of the same class  
person2 = Person("Anne", 30)  

#call member methods of the objects  
person1.showAge()
person2.showName()

Создание объектов в Python довольно простое. Сначала вы указываете имя нового объекта, за которым следует оператор присваивания и имя класса с параметрами (как определено в конструкторе).

Помните, что количество и тип параметров должны быть совместимы с параметрами, полученными в функции-конструкторе.

Когда объект создан, могут быть вызваны методы-члены и доступны атрибуты-члены (при условии, что они доступны).

Ключевые преимущества

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

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

  • Простота. Программы, написанные с применением языков ООП, действительно легко понять. Поскольку все рассматривается как объекты, объектно-ориентированные языки позволяют смоделировать концепцию реального мира.
  • Высокая скорость разработки. Подход ООП предлагает возможность многократного использования классов. Вы можете повторно использовать уже созданные классы вместо того, чтобы записывать их снова. Кроме того, концепция ООП допускает параллельную разработку и использование нескольких классов. Больше усилий прилагается к объектно-ориентированному анализу и проектированию, что также снижает общие затраты на разработку ПО.
  • Удобство тестирования и обслуживания. Поскольку конструкция кода является модульной, часть системы может быть обновлена в случае возникновения проблем без необходимости внесения масштабных изменений. Существующий код легко поддерживать и менять, поскольку новые объекты могут создаваться с небольшими отличиями от существующих. Это делает объектно-ориентированное программирование легко расширяемым — новые функции или изменения в операционной среде могут быть легко внедрены на основе уже существующих.

Объектно-ориентированные языки программирования поставляются с богатыми библиотеками объектов, а код, разработанный в ходе реализации проекта, также может быть повторно использован в будущем при создании других программ. Используя готовые библиотеки, вы можете еще больше ускорить процесс разработки, адаптируя и модифицируя для своих проектов уже существующие рабочие решения. Это особенно полезно при разработке графических интерфейсов пользователя. Поскольку объектные библиотеки содержат много полезных функций, разработчикам программного обеспечения не нужно “изобретать велосипед” так часто, что дает возможность максимально сосредоточиться на создании новой программы.

@ivashkevich

18.09.2019 в 07:43

4069

+10

Mutable default values

Python stores default member variable values in class attributes.
Consider this example, not using Data Classes:

class C:
    x = []
    def add(self, element):
        self.x += element

o1 = C()
o2 = C()
o1.add(1)
o2.add(2)
assert o1.x == 
assert o1.x is o2.x

Note that the two instances of class C share the same class
variable x, as expected.

Using Data Classes, if this code was valid:

@dataclass
class D:
    x: List = []
    def add(self, element):
        self.x += element

it would generate code similar to:

class D:
    x = []
    def __init__(self, x=x):
        self.x = x
    def add(self, element):
        self.x += element

assert D().x is D().x

This has the same issue as the original example using class C.
That is, two instances of class D that do not specify a value for
x when creating a class instance will share the same copy of
x. Because Data Classes just use normal Python class creation
they also share this problem. There is no general way for Data
Classes to detect this condition. Instead, Data Classes will raise a
TypeError if it detects a default parameter of type list,
dict, or set. This is a partial solution, but it does protect
against many common errors. See in the Rejected Ideas section for more details.

Using default factory functions is a way to create new instances of
mutable types as default values for fields:

Вступление

Dataclasses — это новые классы в Python, которые предназначены создания объектов данных (или как их еще называют классов данных). Вы спрашиваете, что такое объекты данных? Вот неполный список функций, которые определяют объекты данных:

  • Они предназначены для хранения данных и тем самым они представлять собой особый тип классов. Например: это может быть просто число или, даже это может быть экземпляр модели ORM. Создавая таким образом особый вид сущности. Так же они могут содержит атрибуты, которые определяют или представляют эту сущность.
  • Их так же можно сравнить с другими объектами того же типа. Например: число может быть больше, меньше или равно другому числу

У Dataclasses есть, конечно, больше возможностей, но этого списка достаточно для начало, чтобы помочь вам понять суть.

Чтобы разобраться с Dataclasses, мы реализуем простой класс, который будет содержат простое число и позволит нам выполнить вышеупомянутые операции. Сначала мы используем обычные классы для этого, а затем сделаем все тоже самое с помощью Dataclasses.

Но прежде чем мы начнем, пару слов о том как создать Dataclasses

В Python 3.7 предоставлен новый декоратор , который используется для преобразования обычного класса в класс данных (dataclass).

Все, что вам нужно сделать, это обернуть класс в декоратор:

from dataclasses import dataclass

@dataclass
class A:
 …

Теперь давайте углубимся в использование Dataclasses.

Метод конструктора

Для инициализации данных используется метод конструктора. Он запускается, когдасоздается объект класса. Он также известен, как метод __init__. Это будет первое определение класса:

classShark:
def__init__(self):
        print("This is the constructor method.")

Если в наш пример добавить к классу Shark указанный метод__init__, программа выведет следующее без внесения изменений в экземпляр sammy:

Вывод
This is the constructor method.
The shark is swimming.
The shark is being awesome.

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

Создадим метод конструктора, который использует переменную name. Ее мы применим для присвоения имен объектам. Мы передадим name в качестве параметра и зададим self.name, равное name:

shark.py

classShark:
def__init__(self, name):
        self.name = name

Можно изменить строки в функциях, чтобы ссылаться на имена:

shark.py

classShark:
def__init__(self, name):
        self.name = name

defswim(self):
# Ссылка на имя
        print(self.name + " is swimming.")

defbe_awesome(self):
# Ссылка на имя
        print(self.name + " is being awesome.")

Также мы можем установить имя объекта Shark sammy равным «Sammy», передав его, как параметр класса Shark:

shark.py

classShark:
def__init__(self, name):
        self.name = name

defswim(self):
        print(self.name + " is swimming.")

defbe_awesome(self):
        print(self.name + " is being awesome.")


defmain():
# Устанавливаем имя объекта Shark
    sammy = Shark("Sammy")
sammy.swim()
sammy.be_awesome()

if __name__ == "__main__":
    main()

Теперь запустим программу:

python shark.py

Вывод
Sammy is swimming.
Sammy is being awesome.

Имя, которое мы передали объекту, выводится. Мы определили метод __init__ с именем параметра (вместе с ключевым словом self) и передали переменную внутри метода.

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

Если нужно было бы добавить еще один параметр, например возраст, мы могли сделать это, передав его методу __init__:

classShark:
def__init__(self, name, age):
        self.name = name
self.age = age

При создании объекта sammy можно передать возраст Сэмми:

sammy = Shark("Sammy", 5)

Чтобы использовать параметр age, также нужно будет создать метод в классе. Методы конструктора позволяют инициализировать определенные атрибуты объекта.

Создание Класса

Создание класса в Пайтоне – это очень просто. Вот простой пример:

Python

# Python 2.x syntax

class Vehicle(object):
«»»docstring»»»

def __init__(self):
«»»Constructor»»»
pass

1
2
3
4
5
6
7
8

# Python 2.x syntax
 

classVehicle(object)

«»»docstring»»»

def__init__(self)

«»»Constructor»»»

pass

Этот класс не делает ничего конкретного, тем не менее, это очень хороший инструмент для изучения. Например, чтобы создать класс, мы используем ключевое слово class, за которым следует наименование класса. В Пайтоне, конвенция указывает на то, что наименование класса должно начинаться с заглавной буквы. Далее нам нужно открыть круглые скобки, за которыми следует слово object и закрытые скобки. «object» — то, на чем основан класс, или наследуется от него. Это называется базовым классом или родительским классом. Большая часть классов в Пайтоне основаны на объекте. У классов есть особый метод, под названием __init__.

Этот метод вызывается всякий раз, когда вы создаете (или создаете экземпляр) объект на основе этого класса. Метод __init__ вызывается единожды, и не может быть вызван снова внутри программы. Другое определение метода __init__ — это конструктор, кстати, этот термин редко встречается в Пайтоне. Вы можете подумать, почему я называю это методом, а не функцией? Функция меняет свое имя на «method», когда она находится внутри класса

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

Вместо этого, мы можем написать это следующим образом:

Python

# Python 3.x syntax

class Vehicle:
«»»docstring»»»

def __init__(self):
«»»Constructor»»»
pass

1
2
3
4
5
6
7
8

# Python 3.x syntax
 

classVehicle

«»»docstring»»»

def__init__(self)

«»»Constructor»»»

pass

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

Python

class Vehicle(object):
«»»docstring»»»

def __init__(self, color, doors, tires):
«»»Constructor»»»
self.color = color
self.doors = doors
self.tires = tires

def brake(self):
«»»
Stop the car
«»»
return «Braking»

def drive(self):
«»»
Drive the car
«»»
return «I’m driving!»

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

classVehicle(object)

«»»docstring»»»

def__init__(self,color,doors,tires)

«»»Constructor»»»

self.color=color

self.doors=doors

self.tires=tires

defbrake(self)

«»»

        Stop the car
        «»»

return»Braking»

defdrive(self)

«»»

        Drive the car
        «»»

return»I’m driving!»

В данном примере мы добавили три атрибута и два метода. Эти три атрибута являются:

Python

self.color = color
self.doors = doors
self.tires = tires

1
2
3

self.color=color

self.doors=doors

self.tires=tires

Атрибуты описывают автомобиль. У него есть цвет, определенное количество дверей и колес. Также у него есть два метода. Метод описывает, что делает класс. В нашем случае, автомобиль может двигаться и останавливаться. Вы могли заметить, что все методы, включая первый, имеют интересный аргумент, под названием self. Давайте рассмотрим его внимательнее.

Замена сеттеров и геттеров на свойство Python

Давайте представим, что у нас есть код, который написал кто-то, кто не очень понимает Python. Как и я, вы скорее всего, видели такого рода код ранее:

Python

# -*- coding: utf-8 -*-
from decimal import Decimal

class Fees(object):
«»»»»»
def __init__(self):
«»»Конструктор»»»
self._fee = None

def get_fee(self):
«»»
Возвращаем текущую комиссию
«»»
return self._fee

def set_fee(self, value):
«»»
Устанавливаем размер комиссии
«»»
if isinstance(value, str):
self._fee = Decimal(value)
elif isinstance(value, Decimal):
self._fee = value

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# -*- coding: utf-8 -*-

fromdecimalimportDecimal

classFees(object)

«»»»»»

def__init__(self)

«»»Конструктор»»»

self._fee=None

defget_fee(self)

«»»

        Возвращаем текущую комиссию
        «»»

returnself._fee

defset_fee(self,value)

«»»

        Устанавливаем размер комиссии
        «»»

ifisinstance(value,str)

self._fee=Decimal(value)

elifisinstance(value,Decimal)

self._fee=value

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

Python

f = Fees()
f.set_fee(«1»)

print(f.get_fee()) # Decimal(‘1’)

1
2
3
4

f=Fees()

f.set_fee(«1»)

print(f.get_fee())# Decimal(‘1’)

Если вам нужно добавить обычную точечную нотацию атрибутов в данный код без выведения из строя всех приложений в этой части кода, вы можете сделать это очень просто, добавив свойство:

Python

# -*- coding: utf-8 -*-
from decimal import Decimal

class Fees(object):
«»»»»»
def __init__(self):
«»»Конструктор»»»
self._fee = None

def get_fee(self):
«»»
Возвращаем текущую комиссию
«»»
return self._fee

def set_fee(self, value):
«»»
Устанавливаем размер комиссии
«»»
if isinstance(value, str):
self._fee = Decimal(value)
elif isinstance(value, Decimal):
self._fee = value

fee = property(get_fee, set_fee)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

# -*- coding: utf-8 -*-

fromdecimalimportDecimal

classFees(object)

«»»»»»

def__init__(self)

«»»Конструктор»»»

self._fee=None

defget_fee(self)

«»»

        Возвращаем текущую комиссию
        «»»

returnself._fee

defset_fee(self,value)

«»»

        Устанавливаем размер комиссии
        «»»

ifisinstance(value,str)

self._fee=Decimal(value)

elifisinstance(value,Decimal)

self._fee=value

fee=property(get_fee,set_fee)

Мы добавили одну строк в конце этого кода. Теперь мы можем делать что-то вроде этого:

Python

f = Fees()
f.set_fee(«1»)
print(f.fee) # Decimal(‘1’)

f.fee = «2»
print( f.get_fee() ) # Decimal(‘2’)

1
2
3
4
5
6

f=Fees()

f.set_fee(«1»)

print(f.fee)# Decimal(‘1’)

f.fee=»2″

print(f.get_fee())# Decimal(‘2’)

Как мы видим, когда мы используем свойство таким образом, это позволяет свойству fee настраивать и получать значение без поломки наследуемого кода. Давайте перепишем этот код с использованием декоратора property, и посмотрим, можем ли мы получить его для разрешения установки.

Python

# -*- coding: utf-8 -*-
from decimal import Decimal

class Fees(object):
«»»»»»
def __init__(self):
«»»Конструктор»»»
self._fee = None

@property
def fee(self):
«»»
Возвращаем текущую комиссию — геттер
«»»
return self._fee

@fee.setter
def fee(self, value):
«»»
Устанавливаем размер комиссии — сеттер
«»»
if isinstance(value, str):
self._fee = Decimal(value)
elif isinstance(value, Decimal):
self._fee = value

if __name__ == «__main__»:
f = Fees()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

# -*- coding: utf-8 -*-

fromdecimalimportDecimal

classFees(object)

«»»»»»

def__init__(self)

«»»Конструктор»»»

self._fee=None

@property

deffee(self)

«»»

        Возвращаем текущую комиссию — геттер
        «»»

returnself._fee

@fee.setter

deffee(self,value)

«»»

        Устанавливаем размер комиссии — сеттер
        «»»

ifisinstance(value,str)

self._fee=Decimal(value)

elifisinstance(value,Decimal)

self._fee=value

if__name__==»__main__»

f=Fees()

Данный код демонстрирует, как создать сеттер для свойства fee. Вы можете делать это, декорируя второй метод, который также называется fee с декоратором, под названием <@fee.setter>. Сеттер будет вызван, когда вы сделаете что-то вроде следующего:

Python

f = Fees()
f.fee = «1»

1
2

f=Fees()

f.fee=»1″

Если вы взгляните на подписи под свойством, то это будут fget, fset, fdel и doc в качестве аргументов. Вы можете создать другой декорируемый метод, используя то же название связи с функцией delet при помощи <@fee.deleter*>*, если вы хотите поймать команду **del для атрибута.

Уничтожение объектов (Сборка мусора)

Python автоматически удаляет ненужные объекты (встроенные типы или экземпляры классов), чтобы освободить пространство памяти. Процесс, с помощью которого Python периодически восстанавливает блоки памяти, которые больше не используются, называется сбором мусора.

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

Счетчик ссылок на объект увеличивается, когда ему присваивается новое имя или он помещается в контейнер (список, кортеж или словарь). Счетчик ссылок объекта уменьшается, когда он удаляется с помощью del , его ссылка переназначается или его ссылка выходит за пределы области видимости. Когда счетчик ссылок объекта достигает нуля, Python собирает его автоматически.

a = 40      # Create object <40>
b = a       # Increase ref. count  of <40> 
c =      # Increase ref. count  of <40> 

del a       # Decrease ref. count  of <40>
b = 100     # Decrease ref. count  of <40> 
c = -1   # Decrease ref. count  of <40> 

Обычно вы не замечаете, когда сборщик мусора уничтожает потерянный экземпляр и освобождает его пространство. Но класс может реализовать специальный метод __del __ () , называемый деструктором, который вызывается, когда экземпляр собирается быть уничтоженным. Этот метод может использоваться для очистки любых ресурсов памяти, используемых экземпляром.

пример

Этот деструктор __del __ () печатает имя класса экземпляра, который должен быть уничтожен —

#!/usr/bin/python

class Point:
   def __init__( self, x=0, y=0):
      self.x = x
      self.y = y
   def __del__(self):
      class_name = self.__class__.__name__
      print class_name, "destroyed"

pt1 = Point()
pt2 = pt1
pt3 = pt1
print id(pt1), id(pt2), id(pt3) # prints the ids of the obejcts
del pt1
del pt2
del pt3

Когда приведенный выше код выполняется, он дает следующий результат —

3083401324 3083401324 3083401324
Point destroyed

Примечание. В идеале вы должны определить свои классы в отдельном файле, а затем импортировать их в основной файл программы с помощью оператора import .

Устойчивость объектов[править]

Объекты всегда имеют своё представление в памяти компьютера и их время жизни не больше времени работы программы. Однако зачастую необходимо сохранять данные между запусками приложения и/или
передавать их на другие компьютеры.
Одним из решений этой проблемы является устойчивость объектов (англ. object persistence) которая достигается с помощью хранения представлений объектов (сериализацией) в виде байтовых последовательностей и их последующего восстановления (десериализация).

Модуль является наиболее простым способом «консервирования» объектов в Python.

Следующий пример показывает, как работает сериализация и десериализация:

# сериализация
>>> import pickle
>>> p = set()
>>> pickle.dumps(p)
'c__builtin__\nset\np0\n((lp1\nI8\naI1\naI2\naI3\naI5\natp2\nRp3\n.'

# де-сериализация
>>> import pickle
>>> p = pickle.loads('c__builtin__\nset\np0\n((lp1\nI8\naI1\naI2\naI3\naI5\natp2\nRp3\n.')
>>> print p
set()

Получаемая при сериализации строка может быть передана по сети, записана в файл или специальное хранилище объектов, а позже — прочитана. Сериализации поддаются не все объекты. Некоторые объекты (например, классы и функции) представляются своими именами, поэтому для десериализации требуется наличие тех же самых классов. Нужно отметить, что нельзя десериализовать данные из непроверенных
источников с помощью модуля , так как при этом возможны практически любые
действия на локальной системе. При необходимости обмениваться данными по незащищенным каналам
или с ненадежными источниками можно воспользоваться другими модулями для сериализации.

В основе сериализации объекта стоит представление его состояния. По умолчанию состояние объекта — это все, что записано в его полях. Пользовательские классы могут управлять сериализацией, предоставляя состояние объекта явным образом (методы , и др.).

На стандартном для Python механизме сериализации построена работа модуля (shelve (англ. глаг.) — ставить на полку; сдавать в архив). Модуль предоставляет функцию
. Объект, который она возвращает, работает аналогично словарю, но объекты сериализуются и сохраняются в файле:

>>> import shelve
>>> s = shelve.open("myshelve.bin")
>>> s'abc' = 1, 2, 3
>>> s.close()
# .....
>>> s = shelve.open("myshelve.bin")
>>> s'abc'
1, 2, 3

Сериализация — не единственная возможная, и подходит не всегда. Для сериализации, не зависящей от языка программирования, можно использовать, например, XML.

Сравнение данных

Как правило, объекты данных необходимо сравнивать друг с другом.

Сравнение между двумя объектами и обычно состоит из следующих операций:

  • a < b
  • a > b
  • a == b
  • a >= b
  • a <= b

В python можно определить в классах, которые могут выполнять вышеуказанные операции. Для простоты, я продемонстрирую лишь реализацию == и <.

Обычный класс

class Number:
    def __init__( self, val = 0):
       self.val = val
 
    def __eq__(self, other):
        return self.val == other.val
 
    def __lt__(self, other):
        return self.val < other.val
@dataclass(order = True)
class Number:
    val: int = 0

Да, вот и все.

Нам не нужно определять методы __eq__ и __lt__, потому что декоратор dataclass автоматически добавляет их в определение класса при вызове с order = True

Ну, как это реализуется?

Когда вы используете dataclass, он добавляет функции __eq__ и __lt__ в определение класса. Мы уже знаем это. Как эти функции знают, что нужно проверить равенство или сделать сравнение?

Сгенерированная функцией __eq__ будет сравнивать кортеж своих атрибутов с кортежем атрибутов другого экземпляра того же класса. В нашем случае вот что эквивалентно автоматически сгенерированной функции __eq__:

def __eq__(self, other):
    return (self.val,) == (other.val,)

Давайте посмотрим на более сложный пример:

Мы напишем класс данных Person, которое будет содержать имя (name) и возраст (age).

@dataclass(order = True)
class Person:
    name: str
    age:int = 0

Автоматически сгенерированный метод __eq__ будет эквивалентен:

def __eq__(self, other):
    return (self.name, self.age) == ( other.name, other.age)

Обратите внимание на порядок атрибутов. Они всегда будут генерироваться в порядке, который вы определили в определении класса данных

Аналогично, эквивалентная функция __le__ будет похожа на:

def __le__(self, other):
    return (self.name, self.age) <= (other.name, other.age)

Необходимость определения функции, подобной __le__, обычно возникает, когда вам нужно отсортировать список ваших объектов данных. Встроенная функция сортировки Python основана на сравнении двух объектов.

>>> import random

>>> a =  #generate list of random numbers

>>> a
>>> 

>>> sorted_a = sorted(a) #Sort Numbers in ascending order
>>> 

>>> reverse_sorted_a = sorted(a, reverse = True) #Sort Numbers in descending order 

>>> reverse_sorted_a
>>> 
Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Adblock
detector