#python #python-3.x #static-methods
#python #статические методы
Вопрос:
предположим следующее определение класса:
class A:
def f(self):
return 'this is f'
@staticmethod
def g():
return 'this is g'
a = A()
Итак, f — это обычный метод, а g — статический метод.
Теперь, как я могу проверить, являются ли функциональные объекты a.f и a.g статическими или нет? Существует ли функция «isstatic» в Python?
Я должен это знать, потому что у меня есть списки, содержащие много разных объектов функции (метода), и для их вызова я должен знать, ожидают ли они «self» в качестве параметра или нет.
Ответ №1:
Давайте немного поэкспериментируем:
>>> import types
>>> class A:
... def f(self):
... return 'this is f'
... @staticmethod
... def g():
... return 'this is g'
...
>>> a = A()
>>> a.f
<bound method A.f of <__main__.A instance at 0x800f21320>>
>>> a.g
<function g at 0x800eb28c0>
>>> isinstance(a.g, types.FunctionType)
True
>>> isinstance(a.f, types.FunctionType)
False
Похоже, его можно использовать types.FunctionType
для различения статических методов.
Комментарии:
1. спасибо, что указали мне на модуль «типы», я почти забыл об этом.
Ответ №2:
Ваш подход кажется мне немного ошибочным, но вы можете проверить атрибуты класса:
(в Python 2.7):
>>> type(A.f)
<type 'instancemethod'>
>>> type(A.g)
<type 'function'>
или атрибуты экземпляра в Python 3.x
>>> a = A()
>>> type(a.f)
<type 'method'>
>>> type(a.g)
<type 'function'>
Комментарии:
1. Обратите внимание, что это работает только в том случае, если объект создан (по крайней мере, в python 3), поэтому, если вы хотите проверить, является ли метод статическим без создания экземпляра объекта, то это не сработает.
2. @DuckPuncher Спасибо! Вы правы, я обновил ответ.
3. @DuckPuncher В Python 3 вы можете проверить
isinstance(A.__dict__['f'], types.FunctionType)
иisinstance(A.__dict__['g'], staticmethod)
Ответ №3:
В дополнение к приведенным здесь ответам, в Python 3 лучший способ выглядит следующим образом:
import inspect
class Test:
@staticmethod
def test(): pass
isstatic = isinstance(inspect.getattr_static(Test, "test"), staticmethod)
Мы используем getattr_static
вместо getattr
, поскольку getattr
будет извлекаться связанный метод или функция, а не staticmethod
объект класса. Вы можете выполнить аналогичную проверку для classmethod
типов и property
(например, атрибутов, определенных с помощью @property
декоратора)
Обратите внимание, что даже если это staticmethod
, не предполагайте, что он был определен внутри класса. Источник метода, возможно, произошел из другого класса. Чтобы получить истинный источник, вы можете посмотреть на полное имя базовой функции и модуль. Например:
class A:
@staticmethod:
def test(): pass
class B: pass
B.test = inspect.getattr_static(A, "test")
print("true source: ", B.test.__qualname__)
Технически, любой метод может использоваться как «статические» методы, при условии, что они вызываются в самом классе, так что просто имейте это в виду. Например, это будет работать отлично:
class Test:
def test():
print("works!")
Test.test()
Этот пример не будет работать с экземплярами Test
, поскольку метод будет привязан к экземпляру и вызываться как Test.test(self)
вместо этого.
Методы экземпляра и класса также могут использоваться как статические методы в некоторых случаях, при условии, что первый аргумент обрабатывается должным образом.
class Test:
def test(self):
print("works!")
Test.test(None)
Возможно, другим редким случаем является staticmethod
, который также привязан к классу или экземпляру. Например:
class Test:
@classmethod
def test(cls): pass
Test.static_test = staticmethod(Test.test)
Хотя технически это a staticmethod
, на самом деле он ведет себя как a classmethod
. Итак, в своем самоанализе вы можете рассмотреть возможность проверки __self__
(рекурсивно на __func__
), чтобы увидеть, привязан ли метод к классу или экземпляру.
Комментарии:
1. Большое спасибо @Azmisov за
get_attr_static
то, что удалось избежать вызова протокола дескриптора! Сегодня я кое-чему научился 🙂
Ответ №4:
Так получилось, что у меня есть модуль для решения этой проблемы. И это решение, совместимое с Python2 / 3. И это позволяет тестировать с помощью метода, наследуемого от родительского класса.
Кроме того, этот модуль также может тестировать:
- обычный атрибут
- метод в стиле свойств
- обычный метод
- staticmethod
- classmethod
Например:
class Base(object):
attribute = "attribute"
@property
def property_method(self):
return "property_method"
def regular_method(self):
return "regular_method"
@staticmethod
def static_method():
return "static_method"
@classmethod
def class_method(cls):
return "class_method"
class MyClass(Base):
pass
Вот решение только для staticmethod. Но я рекомендую использовать модуль, размещенный здесь.
import inspect
def is_static_method(klass, attr, value=None):
"""Test if a value of a class is static method.
example::
class MyClass(object):
@staticmethod
def method():
...
:param klass: the class
:param attr: attribute name
:param value: attribute value
"""
if value is None:
value = getattr(klass, attr)
assert getattr(klass, attr) == value
for cls in inspect.getmro(klass):
if inspect.isroutine(value):
if attr in cls.__dict__:
bound_value = cls.__dict__[attr]
if isinstance(bound_value, staticmethod):
return True
return False
Комментарии:
1. В этом есть ошибка. Он проверяет, является ли статичным любой метод с таким именем в MRO класса. Если в базовом классе есть статический метод, который затеняется нестатическим методом в подклассе, ваша функция будет выводить
True
.2. Я исправил ошибку в этом пакете github.com/MacHu-GWU/inspect_mate-project
Ответ №5:
Зачем беспокоиться? Вы можете просто вызвать g, как вы вызываете f:
a = A()
a.f()
a.g()
Комментарии:
1. вау, действительно, вы правы! Я думал, что это не сработает, потому что у меня есть методы в списке, скажем
l = [a.f, a.g]
, и я вызываю их с помощьюl[0]()
иl[1]()
. Но это работает! Странно, я всегда думал, что обычным методам нужна ссылка на их содержащий объект в качестве первого параметра. Я имею в виду, что если я это сделаюA.f()
, это ошибка, хотяA.g()
работает, хотяA.f(a)
и работает нормально.2. Очевидно, что это хороший ответ (не проверяйте, является ли метод статическим!) на проблему, но он не отвечает на вопрос в названии (проверьте, является ли метод статическим).
3. Ты прав, Иржи. Но я думаю, что хорошо иметь несколько способов решения проблемы, вот почему я опубликовал этот ответ. И, похоже, OP нашел это полезным, так что победа / проигрыш 🙂
4. @DanielJung это работает, потому что, когда вы выполняете a.f, python создает связанный экземпляр метода (он привязан к объекту, для которого вызывается). Когда вы выполняете A.f, он возвращает несвязанный метод (вы все еще можете вызвать его с помощью A.f(a)), в то время как A.g возвращает простую функцию.
5. Зачем беспокоиться? Потому что вы внедряете метакласс, декоратор или пишете инструменты анализа кода.