Understanding self in python

Что такое self?

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

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!»

if __name__ == «__main__»:
car = Vehicle(«blue», 5, 4)
print(car.color)

truck = Vehicle(«red», 3, 6)
print(truck.color)

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

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!»

if__name__==»__main__»

car=Vehicle(«blue»,5,4)

print(car.color)

truck=Vehicle(«red»,3,6)

print(truck.color)

Условия оператора if в данном примере это стандартный способ указать Пайтону на то, что вы хотите запустить код, если он выполняется как автономный файл. Если вы импортировали свой модуль в другой скрипт, то код, расположенный ниже проверки if не заработает. В любом случае, если вы запустите этот код, вы создадите два экземпляра класса автомобиля (Vehicle): класс легкового и класс грузового. Каждый экземпляр будет иметь свои собственные атрибуты и методы. Именно по этому, когда мы выводи цвета каждого экземпляра, они и отличаются друг от друга. Причина в том, что этот класс использует аргумент self, чтобы указать самому себе, что есть что. Давайте немного изменим класс, чтобы сделать методы более уникальными:

Python

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

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

def brake(self):
«»»
Stop the car
«»»
return «%s braking» % self.vtype

def drive(self):
«»»
Drive the car
«»»
return «I’m driving a %s %s!» % (self.color, self.vtype)

if __name__ == «__main__»:
car = Vehicle(«blue», 5, 4, «car»)
print(car.brake())
print(car.drive())

truck = Vehicle(«red», 3, 6, «truck»)
print(truck.drive())
print(truck.brake())

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
30
31

classVehicle(object)

«»»docstring»»»

def__init__(self,color,doors,tires,vtype)

«»»Constructor»»»

self.color=color

self.doors=doors

self.tires=tires

self.vtype=vtype

defbrake(self)

«»»

        Stop the car
        «»»

return»%s braking»%self.vtype

defdrive(self)

«»»

        Drive the car
        «»»

return»I’m driving a %s %s!»%(self.color,self.vtype)

if__name__==»__main__»

car=Vehicle(«blue»,5,4,»car»)

print(car.brake())

print(car.drive())

truck=Vehicle(«red»,3,6,»truck»)

print(truck.drive())

print(truck.brake())

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

Python

car braking
I’m driving a blue car!
I’m driving a red truck!
truck braking

1
2
3
4

car braking

I’m driving a blue car!

I’mdrivingared truck!

truck braking

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

Протокол дескрипторов класса.

descr.__get__(self, obj, type=None) -> value

descr.__set__(self, obj, value) -> None

descr.__delete__(self, obj) -> None

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

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

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

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

Источники

Python NumPy

NumPy IntroNumPy Getting StartedNumPy Creating ArraysNumPy Array IndexingNumPy Array SlicingNumPy Data TypesNumPy Copy vs ViewNumPy Array ShapeNumPy Array ReshapeNumPy Array IteratingNumPy Array JoinNumPy Array SplitNumPy Array SearchNumPy Array SortNumPy Array FilterNumPy Random
Random Intro
Data Distribution
Random Permutation
Seaborn Module
Normal Distribution
Binomial Distribution
Poisson Distribution
Uniform Distribution
Logistic Distribution
Multinomial Distribution
Exponential Distribution
Chi Square Distribution
Rayleigh Distribution
Pareto Distribution
Zipf Distribution

NumPy ufunc
ufunc Intro
ufunc Create Function
ufunc Simple Arithmetic
ufunc Rounding Decimals
ufunc Logs
ufunc Summations
ufunc Products
ufunc Differences
ufunc Finding LCM
ufunc Finding GCD
ufunc Trigonometric
ufunc Hyperbolic
ufunc Set Operations

post-init parameters

In an earlier version of this PEP before InitVar was added, the
post-init function __post_init__ never took any parameters.

The normal way of doing parameterized initialization (and not just
with Data Classes) is to provide an alternate classmethod constructor.
For example:

@dataclass
class C:
    x: int

    @classmethod
    def from_file(cls, filename):
        with open(filename) as fl:
            file_value = int(fl.read())
        return C(file_value)

c = C.from_file('file.txt')

Because the __post_init__ function is the last thing called in the
generated __init__, having a classmethod constructor (which can
also execute code immediately after constructing the object) is
functionally equivalent to being able to pass parameters to a
__post_init__ function.

With InitVars, __post_init__ functions can now take
parameters. They are passed first to __init__ which passes them
to __post_init__ where user code can use them as needed.

The only real difference between alternate classmethod constructors
and InitVar pseudo-fields is in regards to required non-field
parameters during object creation. With InitVars, using
__init__ and the module-level replace() function InitVars
must always be specified. Consider the case where a context
object is needed to create an instance, but isn’t stored as a field.
With alternate classmethod constructors the context parameter is
always optional, because you could still create the object by going
through __init__ (unless you suppress its creation). Which
approach is more appropriate will be application-specific, but both
approaches are supported.

Вызов дескрипторов класса.

Дескриптор может быть вызван непосредственно по имени метода. Например .

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

Детали вызова зависят от того, является ли объектом или классом.

Для объектов механизм вызова находится в , который преобразует в . Реализация работает через цепочку приоритетов, которая дает дескрипторам данных приоритет над переменными экземпляра, переменным экземпляра приоритет над дескрипторами без данных и назначает самый низкий приоритет , если это предусмотрено.

Для классов механизм вызова находится в , который преобразует в . В чистом Python это выглядит так:

def __getattribute__(self, key):
    "Emulate type_getattro() in Objects/typeobject.c"
    v = object.__getattribute__(self, key)
    if hasattr(v, '__get__'):
        return v.__get__(None, self)
    return v

Важно помнить следующее:

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

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

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

Множественное наследование

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

Вот простой пример:

Теперь, если мы создаем экземпляр FooBar, если мы ищем атрибут foo, мы видим, что атрибут Foo находится первым

а также

Вот MRO FooBar:

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

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

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

Для комплексного примера в Python см запись Википедии .

Другая характерная особенность в наследстве является . Супер может получить функции родительских классов.

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

для примера ниже Foo метод инициализировать класс вызывался класс Bar не INIT вызывался

Выход:

Но это не значит , что бар класс не наследуется. Instance конечного класса FooBar также экземпляр класса Bar и класса Foo.

Выход:

Значения по умолчанию

Одна из полезных особенностей — легкость добавления к полям значений по умолчанию. Все ещё не требуется переопределять метод , достаточно указать значения прямо в классе.

Они будут учтены в сгенерированном методе

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

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

The __init__() Function

The examples above are classes and objects in their simplest form, and are
not really useful in real life applications.

To understand the meaning of classes we have to understand the built-in __init__()
function.

All classes have a function called __init__(), which is always executed when
the class is being initiated.

Use the __init__() function to assign values to object properties, or other
operations that are necessary to do when the object
is being created:

Example

Create a class named Person, use the __init__() function to assign values
for name and age:

class Person:  def __init__(self, name, age):   
self.name = name    self.age = agep1 = Person(«John»,
36)print(p1.name)print(p1.age)

Note: The function is called automatically every time the class is being used to create a new object.

Python NumPy

NumPy IntroNumPy Getting StartedNumPy Creating ArraysNumPy Array IndexingNumPy Array SlicingNumPy Data TypesNumPy Copy vs ViewNumPy Array ShapeNumPy Array ReshapeNumPy Array IteratingNumPy Array JoinNumPy Array SplitNumPy Array SearchNumPy Array SortNumPy Array FilterNumPy Random
Random Intro
Data Distribution
Random Permutation
Seaborn Module
Normal Distribution
Binomial Distribution
Poisson Distribution
Uniform Distribution
Logistic Distribution
Multinomial Distribution
Exponential Distribution
Chi Square Distribution
Rayleigh Distribution
Pareto Distribution
Zipf Distribution

NumPy ufunc
ufunc Intro
ufunc Create Function
ufunc Simple Arithmetic
ufunc Rounding Decimals
ufunc Logs
ufunc Summations
ufunc Products
ufunc Differences
ufunc Finding LCM
ufunc Finding GCD
ufunc Trigonometric
ufunc Hyperbolic
ufunc Set Operations

Примеры классов в Python и Java

Для начала давайте реализуем простейший класс в Python и Java, чтобы проиллюстрировать некоторые отличия в этих языках, и будем постепенно вносить в этот класс изменения.
Представим, что у нас есть следующее определение класса Car в Java:

Имя исходного Java-файла должно соответствовать имени хранящегося в нем класса, поэтому мы обязаны назвать файл Car.java. Каждый Java-файл может содержать только один публичный класс.
Такой же класс в Python будет выглядеть так:

В Python вы можете объявить класс где угодно и когда угодно. Сохраним этот файл как car.py.
Используя эти классы как основу, продолжим исследование основных компонентов классов и объектов.

9.6. Приватные переменные

В Python не существует «частных» (приватных) переменных экземпляра, т.е. тех, которые не могут быть доступны, кроме как изнутри объекта. Тем не менее есть соглашение, которое поддерживается большей частью кода Python: идентификатор с префиксом нижней черты (например ) должны рассматриваться как непубличная часть API (будь то функция, метод или элемент данных). Следует учитывать, детали реализации и предмет могут быть изменены без предварительного уведомления.

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

Корректировка имен полезна для того, чтобы позволить подклассам переопределять методы, не нарушая внутриклассовых вызовов методов. Например:

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

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

GPON от МГТС версия 2016 — 2018 года: 16 комментариев

Python Tutorial

Python HOMEPython IntroPython Get StartedPython SyntaxPython CommentsPython Variables
Python Variables
Variable Names
Assign Multiple Values
Output Variables
Global Variables
Variable Exercises

Python Data TypesPython NumbersPython CastingPython Strings
Python Strings
Slicing Strings
Modify Strings
Concatenate Strings
Format Strings
Escape Characters
String Methods
String Exercises

Python BooleansPython OperatorsPython Lists
Python Lists
Access List Items
Change List Items
Add List Items
Remove List Items
Loop Lists
List Comprehension
Sort Lists
Copy Lists
Join Lists
List Methods
List Exercises

Python Tuples
Python Tuples
Access Tuples
Update Tuples
Unpack Tuples
Loop Tuples
Join Tuples
Tuple Methods
Tuple Exercises

Python Sets
Python Sets
Access Set Items
Add Set Items
Remove Set Items
Loop Sets
Join Sets
Set Methods
Set Exercises

Python Dictionaries
Python Dictionaries
Access Items
Change Items
Add Items
Remove Items
Loop Dictionaries
Copy Dictionaries
Nested Dictionaries
Dictionary Methods
Dictionary Exercise

Python If…ElsePython While LoopsPython For LoopsPython FunctionsPython LambdaPython ArraysPython Classes/ObjectsPython InheritancePython IteratorsPython ScopePython ModulesPython DatesPython MathPython JSONPython RegExPython PIPPython Try…ExceptPython User InputPython String Formatting

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

Объекты всегда имеют своё представление в памяти компьютера и их время жизни не больше времени работы программы. Однако зачастую необходимо сохранять данные между запусками приложения и/или
передавать их на другие компьютеры.
Одним из решений этой проблемы является устойчивость объектов (англ. 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.

Настройка класса данных

Кроме параметра , декоратор обладает другими параметрами:

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

Обезьяна Ямочный

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

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

Но как же мы добавим это как метод в ? Это просто мы просто по существу поместить эту функцию в с помощью оператора присваивания.

Почему это работает? Потому что функции — это объекты, как и любой другой объект, а методы — это функции, принадлежащие классу.

Функция должна быть доступна для всех существующих (уже создан) , а также к новым экземплярам

Эти дополнения доступны для всех экземпляров этого класса (или его подклассов) автоматически. Например:

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

9.10. Generator Expressions¶

Some simple generators can be coded succinctly as expressions using a syntax
similar to list comprehensions but with parentheses instead of square brackets.
These expressions are designed for situations where the generator is used right
away by an enclosing function. Generator expressions are more compact but less
versatile than full generator definitions and tend to be more memory friendly
than equivalent list comprehensions.

Examples:

>>> sum(i*i for i in range(10))                 # sum of squares
285

>>> xvec = 10, 20, 30
>>> yvec = 7, 5, 3
>>> sum(x*y for x,y in zip(xvec, yvec))         # dot product
260

>>> unique_words = set(word for line in page  for word in line.split())

>>> valedictorian = max((student.gpa, student.name) for student in graduates)

>>> data = 'golf'
>>> list(datai for i in range(len(data)-1, -1, -1))

Footnotes

Except for one thing. Module objects have a secret read-only attribute called
which returns the dictionary used to implement the module’s
namespace; the name is an attribute but not a global name.
Obviously, using this violates the abstraction of namespace implementation, and
should be restricted to things like post-mortem debuggers.

A complicated example

This code exists in a closed source project:

class Application:
    def __init__(self, name, requirements, constraints=None, path='', executable_links=None, executables_dir=()):
        self.name = name
        self.requirements = requirements
        self.constraints = {} if constraints is None else constraints
        self.path = path
        self.executable_links = [] if executable_links is None else executable_links
        self.executables_dir = executables_dir
        self.additional_items = []

    def __repr__(self):
        return f'Application({self.name!r},{self.requirements!r},{self.constraints!r},{self.path!r},{self.executable_links!r},{self.executables_dir!r},{self.additional_items!r})'

This can be replaced by:

@dataclass
class Application:
    name: str
    requirements: List
    constraints: Dict = field(default_factory=dict)
    path: str = ''
    executable_links: List = field(default_factory=list)
    executable_dir: Tuple = ()
    additional_items: List = field(init=False, default_factory=list)

The Data Class version is more declarative, has less code, supports
typing, and includes the other generated functions.

§2.2 Книги для контент-менеджера и копирайтера

Замена сеттеров и геттеров на свойство 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 для атрибута.

9.4. Некоторые замечания

Атрибуты данных переопределяют атрибуты метода с тем же именем; чтобы избежать случайных конфликтов имен, которые могут привести к трудно обнаруживаемым ошибкам в больших программах, имеет смысл использовать какое-либо соглашение, которое свело бы к минимуму вероятность подобных конфликтов. Это может быть использование большой первой буквы для имен методов, предварение имен атрибутов данных небольшой уникальной строкой (возможно, просто подчеркивание), или использование глаголов для методов и существительных для атрибутов данных.

На атрибуты данных могут ссылаться методы, а также обычные пользовательские («клиенты») объекты. Другими словами, классы не могут быть использованы для реализации чисто абстрактных типов данных. На самом деле ничто в Python не позволяет обеспечить соблюдение сокрытия данных — все что есть основано лишь на соглашениях. (С другой стороны, реализация Python, написанная на C, может полностью скрыть детали реализации и контроль доступа к объекту при необходимости; это может быть использовано расширениями Python, написанными на C.)

Клиенты должны использовать атрибуты данных с осторожностью — клиенты могут испортить постоянные, поддерживаемые методами, «штампуя» на их атрибуты данных

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

Не существует сокращений для того, чтобы сослаться на атрибуты данных (или другие методы!) изнутри методов. Я считаю, что это на самом деле повышает удобство чтения методов: нет шансов запутаться в локальных переменных и переменных экземпляра, когда просматриваешь методы.

Часто первый аргумент метода называется . Это не более, чем соглашение: имя не имеет абсолютно никакого особого значение для Python. Однако следует отметить, что если не следовать соглашению, то ваш код может быть менее читаемым для других программистов Python, и также возможно, что класс browser программы может быть написан, полагаясь на такое соглашение.

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

Теперь и , и , и являются атрибутами класса C, который ссылается на функции-объекты, и, следовательно, все они являются методами экземпляров класса C — есть точный эквивалент

Обратите внимание, что эта практика, как правило, способна лишь запутать читателя программы

Методы могут вызывать другие методы путем использования аргумента :

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

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

dataclass как вызываемый декоратор

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

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

Из официальных декоратор может использоваться как вызываемый со следующими аргументами:

@dataclass(init=True, repr=True, eq=True, order=False, unsafe_hash=False, frozen=False)
class C:
 …
  1. : По умолчанию будет создан метод __init__. Если ему передано значение False, у класса не будет метода __init__.
  2. : Метод __repr__ генерируется по умолчанию. Если ему передано значение False, у класса не будет метода __repr__.
  3. : По умолчанию будет создан метод __eq__. Если ему передано значение False, метод __eq__ не будет добавлен классом данных, но по умолчанию все равное будет object.__eq__.
  4. : По умолчанию генерируются методы __gt__, __ge__, __lt__, __le__. Если будет False, они не будут заданы.

Аргумент frozen мы обсудим чуть позже. Аргумент unsafe_hash заслуживает отдельного поста из-за его сложных вариантов использования.

Сейчас вернемся к нашему примеру использования, вот что нам нужно:

1. 2. 

Эти функции генерируются по умолчанию, поэтому нам не нужно генерировать другие функции. Как нам это сделать? Просто передайте соответствующим аргументам значение False.

@dataclass(repr = False) # order, unsafe_hash and frozen are False
class Number:
    val: int = 0

>>> a = Number(1)

>>> a
>>> <__main__.Number object at 0x7ff395afe898>

>>> b = Number(2)

>>> c = Number(1)

>>> a == b
>>> False

>>> a < b
>>> Traceback (most recent call last):
 File “<stdin>”, line 1, in <module>
TypeError: ‘<’ not supported between instances of ‘Number’ and ‘Number’
Добавить комментарий

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

Adblock
detector