Как динамически клонировать методы класса, но уметь отличать их друг от друга в Python

#python #class

#python #класс

Вопрос:

Я пытаюсь написать оболочку для внешнего модуля на Python. Модуль предоставляет метод для сопряжения глагола, который ожидает 2 аргумента. Я хотел бы объединить это в несколько методов, и мне было интересно, есть ли способ сделать это программно.

т.е. вместо:

 class X:
  def a(self,arg):
    return module.do(arg,'a')
  def b(self,arg):
    return module.do(arg,'b')
  ...
  def z(self,arg):
    return module.do(arg,'z')
  

Я пытался сделать:

 class X:
  def a(self,arg):
    return module.do(arg,__name__)
    return module.do(arg,__name__)
  def __init__(self):
    setattr(self,'b',self.a)
    ...
    setattr(self,'z',self.a)
x = X()
x.a(y)
x.b(y)
x.z(y)
  

Проблема в том, что name возвращает метод верхнего уровня, а не текущий.
Я пробовал оба:

 from inspect import stack
stack()[0][3]
import sys
sys._getframe().f_code.co_name
  

Но когда я вызываю b () или z () Я получаю ‘a’.
Я делаю что-то не так? Есть ли какой-либо другой способ добиться аналогичного результата?

Комментарии:

1. Почему бы просто не обернуть «do» новой функцией, которая принимает строковый аргумент?

Ответ №1:

Вы можете сделать что-то вроде

 class X:
    pass
for name in "abcdefghij":
    setattr(X, name, lambda self, arg, _name=name: module.do(arg, _name))
  

Достаточно добавить методы в класс один раз — нет необходимости делать это для каждого экземпляра в __init__() .

Возможно, лучшей альтернативой является перезапись __getattr__() :

 class X:
    def __getattr__(self, name):
        def foo(arg):
            return module.do(arg, name)
        return foo
  

Комментарии:

1. Второй вариант — это то, что я искал. Спасибо.

Ответ №2:

Вы просто устанавливаете все атрибуты вашего класса b to z так, чтобы они указывали на a метод. Это означает, что всякий раз, когда кто-то обращается к instance.b , он возвращает a метод (вот почему co_name всегда a ).

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

 import string

class X(object):
  def __getattr__(self, name):
    if name in string.lowercase and len(name) == 1:
      def call_into_module(arg):
        return module.do(arg, name)
      return call_into_module

    return super(X, self).__getattr__(name)
  

Комментарии:

1. Это именно то, что мне было нужно. Большое вам спасибо. Я понятия не имел, что смогу перезаписать getattr

2. @Cameron: name in string.lowercase Предназначен для проверки, name есть ли одна строчная буква? Это не так.

3. @Sven: Лучше? Спасибо, что обратили на меня внимание по этому поводу 🙂

4. @Cameron: Я бы использовал name.islower() and len(name) == 1 или name in list(string.lowercase) . a , b c Именование методов в исходном сообщении в любом случае приведено только для примера, так что на самом деле это не имеет значения. SO предназначен для придирок, поэтому я не смог удержаться. 🙂

5. @Cameron: ты делаешь super неправильно. Так и должно быть super(X, self) . Это важное различие, потому что смысл super в том, что вам не нужно жестко кодировать суперклассы в коде при вызове их методов.