Рубрики
Без рубрики

MetaProgramming Python – метод отсутствует

Реализация метода «Mething_Missing» Ruby в Python. Теги с Python, Ruby, MetaProgramming.

Я читаю книгу под названием MetaProgramming Ruby Паоло Перрота, и это действительно интересно! Я многомую много. Каждый так часто, как я читаю, я сталкиваюсь с техникой И я думаю о себе: «Это аккуратно! Интересно, могу ли я сделать что-то похожее на другом языке, как Python? ” После некоторых исследований я думал, что подим одно такое открытие со всеми остальными. Но сначала, какой-то фон.

Фон: что такое метапрограммирование?

MetaProgramming – это аккуратная вещь, которую может сделать некоторые (все? Не уверен) динамические языки. По сути, это куча различных способов написания кода, который будет во время выполнения, напишите ваш код для вас. Давайте посмотрим, чтобы увидеть, что я имею в виду.

>>> class Dude:
...     def __init__(self):
...         pass
...
...     def sup(self):
...         return "Sup, brah!"
>>> d = Dude()
>>> dir(d)
[ ...(lots of built-in methods, etc.), 'sup']

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

>>> if 'sup' in dir(d):
...     d.name = "Brad"
... else:
...     d.name = "Chad"
...
>>> d.name
'Brad'

Теперь мы изменили наши объекты на основе методов и атрибутов тех же объектов. Наш код вызвал написанный код (немного). ПОЧУВСТВУЙ СИЛУ.

Техника: метод отсутствует

Если вы знакомы с Ruby, вы можете знать, что объекты Ruby имеют встроенный метод под названием method_missing Это вызывается – предсказуемо – если вы вызываете метод, который не существует внутри этого конкретного объекта. Вы можете сделать всевозможные вещи с этим. Один состоит в том, чтобы динамически генерировать методы во время выполнения на основе вещей из вашего контроля. Это экономит на козе котельной и постоянной рефакторинги. Нам нужен пример, чтобы сделать все более понятно, хотя.

Сценарий: Crazy Designer API!

Допустим, вы работаете с товарищем по команде, который выпивает слишком много кофе. Они управляют API, что ваша программа потребляет, но они продолжают добавлять и изменять имена конечных точек. Макет всегда одинаково, хотя, так что, по крайней мере, предсказуемо. Например, в прошлый понедельник, основной ресурс был Свекла и так вы реализовали get_beets () Метод на вашем Супермаркет класс. Этот метод извлекает ток Свекла Объекты в инвентаре …

class Supermarket:
    def __init__(self, api_root):
        self.api_root = api_root

    def get_beets(self, *args, **kwargs):
        url = f'{self.api_root}/beets/'
        return self.api_get(url, *args, **kwargs)

    def api_get(self, url, *args, **kwargs):
        ...
        # Imagine this method hits the api with the provided arguments
        print(f'{url} called with args: {args}')  # for this example

По крайней мере … до тех пор, пока yahoo не решил использовать чуть более описательное название объекта: Жик Отказ Итак, вы вернетесь к вашему коду и обновите имя метода и вызов API. Тогда они добавляют rotabegas На лету, хотя большая часть вашего кода одинакова. Но! Вы подлым, вы умны, и вы знаете о MetaProgramming Действительно И так вы добираетесь до вашего Супермаркет ‘s __getattr__ метод.

import re

class Supermarket:
    def __init__(self, api_root):
        self.api_root = api_root
        self.pattern = re.compile(r'get_([a-z]+)')  # matches any get_[something] call

    def __getattr__(self, method_name):
        match = re.match(self.pattern, method_name)
        if match:
            def temp_method(*args, **kwargs):
                url = f'{self.api_root}/{match.group(1)}/'
                return self.api_get(url, *args, **kwargs)
            return temp_method
        else:
            raise AttributeError(f'No such attribute: {method_name}')

# Let's test it!
>>> s = Supermarket('example.com')
>>> s.get_beetroots(3)
'example.com/beetroots Called with args: (3)'
>>> s.this_should_error()
...
AttributeError: No such attribute: this_should_error

Что только что произошло? Давайте ударим ключевые моменты:

  1. __getattr__ это встроенный метод в объектах Python, который вызывается, если Python не может найти метод или атрибут, который вы ищете. Мы поговорим об этом немного больше через минуту. Но Python ожидает, что этот метод будет возвращать функцию, которая будет называться, либо поднять атрибутрэррез, как обычно. Как вы можете видеть, это то, что мы сделали выше.
  2. Мы определили Регулярное выражение Чтобы сопоставить шаблон метода вызова API, который мы ожидаем. Мы не знаем, что будет объект/конечная точка, но мы знаем, что он начнет с «get_» и заканчивается именем объекта.
  3. __getattr__ передается имя метода, который вызывается. Если имя метода соответствует нашему регулярному выражению, мы переходим к шагу 4. В противном случае мы продолжаем привлечение AttributeError. Это показано в последнем вызове all для this_should_Error Отказ
  4. Если этот метод вызова соответствует нашему регулярному выражению, мы хотим создать и вернуть функцию для вызова. Это не обязательно имеет значение, что вы называете этой внутренней функцией. Это может, если мы решили навсегда добавить его в наш класс, но мы не делаем этого сейчас. Эта функция устанавливает котельной, которую мы стремимся избегать. Это должно принять параметры, которые мы ожидали, такие как get_beets принять.
  5. Match.group (1) Возвращает элемент в первой (и только) группе скобок в нашем Regex, что происходит как имя объектов, которые мы заботимся о.
  6. Наконец, мы возвращаем функцию, которая сразу же называется аргументами, которые пользователь изначально указан.

В идеальном мире мы должны иметь Супермаркет Получите список доступных конечных точек, поэтому мы можем предупредить пользователя, если они сделают API для чего-то, что не является доступной конечной точкой. Это может также помочь в безопасности некоторых. Я оставлю это как упражнение для читателя.

Переопределение методов __getattr __

У меня есть еще два возможных сценария для вас. Оба имеют одно и то же решение.

Сценарий: пример метода или переопределения

Что, если вы хотите выложить пример метода, чтобы люди, которые читают ваш код, могут увидеть пример того, как будет выглядеть один из динамически сгенерированных методов? ИЛИ. Что, если вы хотите переопределить метод, определяя свое собственное поведение. Оба возможны, поскольку __getattr__ Только вызывается после того, как Python ищет объект для желаемого атрибута. Посмотри.

class Supermarket:
    ...
    def get_squash(self, *args, **kwargs):
        return "NO.  NO SQUASH.  It is the devil's gourd."

    def get_peanuts(self, *args, **kwargs):
        """
        Example of a dynamically generated API call method
        created by __getattr__
        """
        url = f'{self.api_root}/peanuts/'
        return self.api_get(url, *args, **kwargs)

>>> s = Supermarket('localhost')
>>> s.get_squash()
"NO.  NO SQUASH.  It is the devil's gourd."
>>> s.get_peanuts()
'localhost/peanuts called with args: []'
>>> s.get_bananas('big ones')
'localhost/bananas called with args: ["big ones"]'
>>> s.soup()
...
raise AttributeError(f'No such attribute: {method_name}')

Все это работает как запланировано!

Мета

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

В целом, дайте ему попробовать, и, возможно, это будет полезный совет для вас! Дайте мне знать, как это происходит 😁

Слияние – Я действительно рекомендую проверить MetaProgramming Ruby Книга я упомянул в начале этой статьи.

P.P.S. – Если вы нашли себя иду “, что F – эти F-струны (например, F' Это « » )”, не бойся. Они самые новые строковые интерполяции/форматирования Python. У меня есть планы написать сообщение о них в ближайшее время. Они похожи на новый javaScript # {синтаксис}

обложка: Шейн Уиллис

Первоначально опубликовано мой блог

Оригинал: “https://dev.to/rpalo/metaprogramming-python—method-missing”