Введение в встроенные методы-декораторы

PythonPythonBeginner
Практиковаться сейчас

This tutorial is from open-source community. Access the source code

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

Введение

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

Предопределенные декораторы

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

class Foo:
    def bar(self,a):
     ...

    @staticmethod
    def spam(a):
     ...

    @classmethod
    def grok(cls,a):
     ...

    @property
    def name(self):
     ...

Рассмотрим их по порядку.

Статические методы

@staticmethod используется для определения так называемых статических методов класса (из C++/Java). Статический метод - это функция, которая является частью класса, но которая не работает с экземплярами.

class Foo(object):
    @staticmethod
    def bar(x):
        print('x =', x)

>>> Foo.bar(2) ## x=2
>>>

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

Методы класса

@classmethod используется для определения методов класса. Метод класса - это метод, который получает объект класса в качестве первого параметра вместо экземпляра.

class Foo:
    def bar(self):
        print(self)
    @classmethod
    def spam(cls):
        print(cls)

>>> f = Foo()
>>> f.bar()
<__main__.Foo object at 0x971690>   ## Экземпляр `f`
>>> Foo.spam()
<class '__main__.Foo'>              ## Класс `Foo`
>>>

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

class Date:
    def __init__(self,year,month,day):
        self.year = year
        self.month = month
        self.day = day
    @classmethod
    def today(cls):
        ## Обратите внимание, как класс передается в качестве аргумента
        tm = time.localtime()
        ## И используется для создания нового экземпляра
        return cls(tm.tm_year, tm.tm_mon, tm.tm_mday)

d = Date.today()

Методы класса решают некоторые сложные проблемы с такими функциями, как наследование.

class Date:
  ...
    @classmethod
    def today(cls):
        ## Получает правильный класс (например, `NewDate`)
        tm = time.localtime()
        return cls(tm.tm_year, tm.tm_mon, tm.tm_mday)

class NewDate(Date):
  ...

d = NewDate.today()

Упражнение 7.11: Методы класса на практике

В файлах report.py и portfolio.py создание объекта Portfolio немного запутано. Например, в программе report.py есть код такого вида:

def read_portfolio(filename, **opts):
    '''
    Считывает файл портфеля акций в список словарей с ключами
    name, shares и price.
    '''
    with open(filename) as lines:
        portdicts = fileparse.parse_csv(lines,
                                        select=['name','shares','price'],
                                        types=[str,int,float],
                                        **opts)

    portfolio = [ Stock(**d) for d in portdicts ]
    return Portfolio(portfolio)

а в файле portfolio.py Portfolio() определяется с странным инициализатором такого вида:

class Portfolio:
    def __init__(self, holdings):
        self._holdings = holdings
  ...

Честно говоря, цепочка ответственности несколько запутана, потому что код рассеян. Если класс Portfolio должен содержать список экземпляров Stock, возможно, стоит изменить класс, чтобы он был более понятным. Так:

## portfolio.py

import stock

class Portfolio:
    def __init__(self):
        self._holdings = []

    def append(self, holding):
        if not isinstance(holding, stock.Stock):
            raise TypeError('Expected a Stock instance')
        self._holdings.append(holding)
  ...

Если вы хотите прочитать портфель из CSV-файла, возможно, стоит сделать для этого метод класса:

## portfolio.py

import fileparse
import stock

class Portfolio:
    def __init__(self):
        self._holdings = []

    def append(self, holding):
        if not isinstance(holding, stock.Stock):
            raise TypeError('Expected a Stock instance')
        self._holdings.append(holding)

    @classmethod
    def from_csv(cls, lines, **opts):
        self = cls()
        portdicts = fileparse.parse_csv(lines,
                                        select=['name','shares','price'],
                                        types=[str,int,float],
                                        **opts)

        for d in portdicts:
            self.append(stock.Stock(**d))

        return self

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

>>> from portfolio import Portfolio
>>> with open('portfolio.csv') as lines:
...     port = Portfolio.from_csv(lines)
...
>>>

Примените эти изменения к классу Portfolio и измените код report.py, чтобы использовать метод класса.

✨ Проверить решение и практиковаться

Резюме

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