Метод класса Python запускается при вызове другого метода

У меня есть класс следующим образом:

class MyClass(object):
    def __init__(self):
        self.foo = "foo"
        self.bar = "bar"
        self.methodCalls = 0  #tracks number of times any function in the instance is run

    def get_foo(self):
        addMethodCall()
        return self.foo

    def get_bar(self):
        addMethodCall()
        return self.bar

    def addMethodCall(self):
        self.methodCalls += 1

Есть ли встроенная функция, которая вызывается всякий раз, когда метод вызывается вместо того, чтобы постоянно работатьaddMethodCall()?

 techydesigner15 июн. 2016 г., 12:11
@MartijnPieters Я пытаюсь подсчитать общее количество разget_foo() а такжеget_bar() были вызваны в конкретном экземпляре класса.
 techydesigner15 июн. 2016 г., 12:08
@MartijnPieters Вопрос уточнен.
 jonrsharpe15 июн. 2016 г., 12:09
Вы могли бы подключиться к__getattribute__, но я бы подумал, декоратор@count_calls на методы, которые вы хотите отслеживать, было бы более точным решением.
 techydesigner15 июн. 2016 г., 12:10
@MartijnPieters Обычные методы - я имею в видуfoobar.addMethodCall() - просто функция внутри класса.
 techydesigner15 июн. 2016 г., 12:06
@MartijnPieters Я уточню вопрос и найду лучший пример.
 Martijn Pieters15 июн. 2016 г., 12:11
Это не методы класса, это просто методы; вы пытаетесь посчитать вызовы методаза экземпляр вот думаю.
 techydesigner15 июн. 2016 г., 12:25
@MartijnPietersgettattr был плохой пример
 Martijn Pieters15 июн. 2016 г., 12:09
__getattribute__ доступ длявсе атрибут доступа. Вы, вероятно, должны очистить свою терминологию;метод класса это@classmethodдескриптор Вы говорите орегулярный методы здесь?
 Mixone15 июн. 2016 г., 12:32
используя getattribute с callable (), вы можете увеличивать счетчик только при вызове метода или напрямую с помощью if при переданном имени
 Martijn Pieters15 июн. 2016 г., 12:05
Методы тоже атрибуты, и__getattr__ доступен только когда атрибутотсутствует.

Ответы на вопрос(1)

Решение Вопроса

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

вызов к объекту метода тогда отдельный шаг от создания объекта метода:

>>> class Foo(object):
...     def bar(self):
...         return 'bar method on Foo'
...
>>> f = Foo()
>>> f.bar
<bound method Foo.bar of <__main__.Foo object at 0x100777bd0>>
>>> f.bar is f.bar
False
>>> stored = f.bar
>>> stored()
'bar method on Foo'

Это задачаobject.__getattribute__() метод чтобы вызвать протокол дескриптора, чтобы вы могли подключиться к нему, чтобы увидеть, когда метод создается, но вам все равно нужнозаворачивать который произвел метод объекта для обнаружениязвонки, Вы можете вернуть объект с__call__ метод что прокси для фактического метода, например.

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

from functools import wraps

def method_counter(func):
    @wraps(func)
    def wrapper(self, *args, **kwargs):
        self.methodCalls += 1
        return func(self, *args, **kwargs)
    return wrapper

Вам все еще нужно применить это ко всем функциям в вашем классе. Вы можете применить это вручную ко всем методам, которые вы хотите посчитать:

class MyClass(object):
    def __init__(self):
        self.foo = "foo"
        self.bar = "bar"
        self.methodCalls = 0  #tracks number of times any function method is run

    @method_counter
    def get_foo(self):
        return self.foo

    @method_counter
    def get_bar(self):
        return self.bar

или вы можете использовать метакласс:

import types

class MethodCounterMeta(type):
    def __new__(mcls, name, bases, body):
        # create new class object
        for name, obj in body.items():
            if name[:2] == name[-2:] == '__':
                # skip special method names like __init__
                continue
            if isinstance(obj, types.FunctionType):
                # decorate all functions
                body[name] = method_counter(obj)
        return super(MethodCounterMeta, mcls).__new__(mcls, name, bases, body)

    def __call__(cls, *args, **kwargs):
        # create a new instance for this class
        # add in `methodCalls` attribute
        instance = super(MethodCounterMeta, cls).__call__(*args, **kwargs)
        instance.methodCalls = 0
        return instance

Это заботится обо всем, что нужно декоратору, устанавливаяmethodCalls атрибут для вас, поэтому ваш класс не должен:

class MyClass(object):
    __metaclass__ = MethodCounterMeta
    def __init__(self):
        self.foo = "foo"
        self.bar = "bar"

    def get_foo(self):
        return self.foo

    def get_bar(self):
        return self.bar

Демонстрация последнего подхода:

>>> class MyClass(object):
...     __metaclass__ = MethodCounterMeta
...     def __init__(self):
...         self.foo = "foo"
...         self.bar = "bar"
...     def get_foo(self):
...         return self.foo
...     def get_bar(self):
...         return self.bar
...
>>> instance = MyClass()
>>> instance.get_foo()
'foo'
>>> instance.get_bar()
'bar'
>>> instance.methodCalls
2

Приведенный выше метакласс учитывает толькофункциональные объекты (так что результатdef заявления иlambda выражения) часть тела класса для украшения. Он игнорирует любые другие вызываемые объекты (есть еще типы, которые имеют__call__ метод, такой какfunctools.partial объекты), а также функции, добавленные в класспозже.

 techydesigner15 июн. 2016 г., 12:43
@MartijnPieters ..и если__init__ на самом деле был вызван, будет ли возвращать ошибку?
 Mixone15 июн. 2016 г., 13:37
Я чувствовал, что должен был удалить его, потому что, как вы сказали, я забыл принять это во внимание ^^ (потребовалось много времени, чтобы ответить извините, был занят)
 Martijn Pieters15 июн. 2016 г., 12:53
@Mixone: спасибо, что хотя бы опубликовали это как ответ; Мне жаль, что вы решили удалить его. У этого подхода были огромные недостатки: вы считали доступ к атрибутам (отфильтрованный по вызываемым объектам), а не к вызовам. В своем ответе я показал, почему это различие важно.
 Martijn Pieters15 июн. 2016 г., 12:42
@techydesigner: это так; в противном случае__init__ Метод тоже считается.
 Mixone15 июн. 2016 г., 12:40
я не согласен с утверждением «нет хуков», это вполне возможно сделать
 Martijn Pieters15 июн. 2016 г., 12:45
@Mixone: тогдаопубликовать это как ответ, Но просто тестирование, что-топодлежащий выкупу не значит, что это на самом делебыть называется.
 Martijn Pieters15 июн. 2016 г., 12:44
@techydesigner: нет, не будет.__init__ это просто еще один метод в этом отношении.
 TheLazyScripter15 июн. 2016 г., 12:45
@Mixone: Итак, напишите свое решение и опубликуйте собственный ответ.
 Kapil Garg06 апр. 2017 г., 21:13
С метаклассом все было отлично.
 Mixone15 июн. 2016 г., 12:44
Нет, я не согласен с ответом в том смысле, что вы заявляете, что никто не может подключиться к нему, любой метод является атрибутом, и любой метод может быть вызван, поэтому, если я вызываю object .__ getattribute__, я могу получить правильное функционирование внутреннего метода и проверить, если атрибут может быть вызван, сочетание обоих дает именно то, что нужно спрашивающему, в 5 строках кода
 techydesigner15 июн. 2016 г., 12:41
@Mixone В любом случае, он отлично отвечает на мой вопрос. Также я предполагаю, что модификатор для специальных методов является необязательным?
 Martijn Pieters15 июн. 2016 г., 12:40
@Mixone: нетпрямые зацепки за класс, Вы должны понимать, как создаются методы и как вместо этого работают вызовы этих объектов.

Ваш ответ на вопрос