Лучший способ вызвать методы в объявлениях класса Python? - PullRequest
4 голосов
/ 20 ноября 2008

Скажем, я объявляю класс C, и некоторые объявления очень похожи. Я хотел бы использовать функцию f, чтобы уменьшить повторение кода для этих объявлений. Можно просто объявить и использовать f как обычно:

>>> class C(object):
...     def f(num):
...             return '<' + str(num) + '>'
...     v = f(9)
...     w = f(42)
... 
>>> C.v
'<9>'
>>> C.w
'<42>'
>>> C.f(4)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unbound method f() must be called with C instance as first argument (got int instance instead)

Oops! Я случайно представил f внешнему миру, но он не принимает аргумент self (и не может по понятным причинам). Одна из возможностей будет del функция после того, как я ее использую:

>>> class C(object):
...     def f(num):
...             return '<' + str(num) + '>'
...     v = f(9)
...     del f
... 
>>> C.v
'<9>'
>>> C.f
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: type object 'C' has no attribute 'f'

Но что, если я захочу снова использовать f позже, после объявления? Это не будет делать, чтобы удалить функцию. Я мог бы сделать его «приватным» (то есть, добавить к его имени __) и назначить ему обработку @staticmethod, но вызов объектов staticmethod через ненормальные каналы становится очень прикольным:

>>> class C(object):
...     @staticmethod
...     def __f(num):
...             return '<' + str(num) + '>'
...     v = __f.__get__(1)(9)   # argument to __get__ is ignored...
... 
>>> C.v
'<9>'

Я должен использовать вышеупомянутое безумие, потому что staticmethod объекты, которые являются дескрипторами, сами по себе не могут быть вызваны. Мне нужно восстановить функцию, обернутую объектом staticmethod, прежде чем я смогу ее вызвать.

Должен быть лучший способ сделать это. Как я могу чисто объявить функцию в классе, использовать ее во время объявления, а также использовать ее позже из класса? Должен ли я даже делать это?

Ответы [ 6 ]

14 голосов
/ 20 ноября 2008

Проще говоря, решение состоит в том, что f не обязательно должен быть членом класса. Я предполагаю, что ваш мыслительный процесс прошел через фильтр языка Java, вызывающий ментальный блок. Это выглядит примерно так:

def f(n):
    return '<' + str(num) + '>'

class C(object):

    v = f(9)
    w = f(42)

Тогда, когда вы захотите снова использовать f, просто используйте его

>>> f(4)
'<4>'

Я думаю, что мораль сказки такова: «В Python у вас нет , чтобы заставить все в классе»

3 голосов
/ 20 ноября 2008

Расширяя Али ответ, если вы действительно хотите избежать f в пространстве имен модуля (и использование неэкспортированного имени, такого как _f, или установки __all__ недостаточно), тогда этого можно достичь, создав класс в замыкании.

def create_C():
    def f(num):
        return '<' + str(num) + '>'

    class C(object):
        v = f(9)
        def method_using_f(self, x):  return f(x*2)
    return C

C=create_C()
del create_C

Таким образом, C имеет доступ к f в пределах своего определения и методов, но больше ничего не делает (за исключением довольно сложного самоанализа его методов (C.method_using_f.im_func.func_closure))

Это, вероятно, излишне для большинства целей - документирование того, что f является внутренним с использованием соглашения об именовании префиксов "_", должно как правило, достаточно.

[Редактировать] Еще один вариант - хранить ссылку на предварительно обернутый функциональный объект в методах, в которых вы хотите его использовать. Например, устанавливая его в качестве аргумента по умолчанию:

class C(object):
    def f(num):
        return '<' + str(num) + '>'

    v = f(9)
    def method_using_f(self, x, f=f):  return f(x*2)

    del f

(хотя я думаю, что подход к закрытию, вероятно, лучше)

2 голосов
/ 20 ноября 2008

Я полагаю, вы пытаетесь сделать это:

class C():
...     class F():
...         def __call__(self,num):
...             return "<"+str(num)+">"
...     f=F()
...     v=f(9)
>>> C.v
'<9>'
>>> C.f(25)
'<25>'
>>> 

Может быть, есть лучшее или более питонное решение ...

"объявить функцию в классе, использовать ее во время ее объявления, а также использовать ее позже из класса"

Извините. Не может быть сделано.

«Не может быть сделано», кажется, не ладит с Python

2 голосов
/ 20 ноября 2008

Это одна возможность:

class _C:
    # Do most of the function definitions in here
    @classmethod
    def f(cls):
        return 'boo'

class C(_C):
    # Do the subsequent decoration in here
    v = _C.f()
1 голос
/ 21 ноября 2008

Один вариант: лучше написать staticmethod:

class staticfunc(object):
    def __init__(self, func):
        self.func = func
    def __call__(self, *args, **kw):
        return self.func(*args, **kw)
    def __repr__(self):
        return 'staticfunc(%r)' % self.func
0 голосов
/ 20 ноября 2008

Давайте начнем с самого начала.

"объявить функцию в классе, использовать ее во время ее объявления, а также использовать ее позже из класса"

Извините. Не может быть сделано «В классе» противоречит «используемому во время объявления».

  • В классе означает, созданный как часть объявления.
  • Используется во время объявления означает, что он существует вне класса. Часто как мета класс. Однако есть и другие способы.

Непонятно, какими должны быть C.w и C.v. Они просто струны? Если это так, внешняя функция f является лучшим решением. «Не загромождайте пространство имен» - это немного странно. В конце концов, вы хотите использовать его снова.

Он находится в том же модуле, что и C. Поэтому в Python есть модули. Он связывает функцию и класс вместе.

import myCmod

myCmod.C.w
myCmod.C.v
myCmod.f(42)

Если w и v не простые строки, есть действительно хорошее решение, которое дает большую гибкость.

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

>>> class F(object):
    def __init__( self, num ):
        self.value= num
        self.format= "<%d>" % ( num, )

>>> class C(object):
    w= F(42)
    v= F(9)

>>> C.w
<__main__.F object at 0x00C58C30>
>>> C.w.format
'<42>'
>>> C.v.format
'<9>'

Преимущество этого в том, что F - это правильная вещь первого класса, которую можно расширить. Не «скрытая» вещь, которую мы пытаемся избежать разоблачения. Это факт жизни, поэтому мы могли бы также следовать принципу Open / Closed и сделать его открытым для расширения.

...