Python: как ссылаться на класс изнутри (например, рекурсивная функция) - PullRequest
22 голосов
/ 10 января 2010

Для рекурсивной функции мы можем сделать:

def f(i):
  if i<0: return
  print i
  f(i-1)

f(10)

Однако есть ли способ сделать следующее?

class A:
  # do something
  some_func(A)
  # ...

Ответы [ 11 ]

18 голосов
/ 10 января 2010

В Python вы не можете ссылаться на класс в теле класса, хотя в таких языках, как Ruby, это возможно

Вместо этого в Python вы можете использовать декоратор класса, но он будет вызываться после инициализации класса. Другим способом может быть использование метакласса, но это зависит от того, чего вы пытаетесь достичь.

8 голосов
/ 10 января 2010

Вы не можете с конкретным синтаксисом, который вы описываете из-за времени, в которое они оцениваются. Причина, по которой работает пример функции, заключается в том, что вызов f (i-1) в теле функции вызван тем, что разрешение имен f не выполняется до тех пор, пока функция не будет фактически вызвана , На данный момент f существует в пределах объема выполнения, так как функция уже была оценена. В случае примера класса ссылка на имя класса ищется во время оценки определения класса. Как таковой, он еще не существует в локальной области.

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

class MetaA(type):

    def __init__(cls):
        some_func(cls)

class A(object):
    __metaclass__=MetaA
  # do something
  # ...

Используя этот подход, вы можете выполнять произвольные операции над объектом класса во время оценки класса.

2 голосов
/ 03 мая 2019

Если я правильно понимаю ваш вопрос, вы сможете сослаться на класс A внутри класса A, поместив аннотацию типа в кавычки. Это называется прямой ссылкой.

class A:
  # do something
  def some_func(self, a: 'A')
  # ...

См. Ссылку ниже

  1. https://github.com/python/mypy/issues/3661
  2. https://www.youtube.com/watch?v=AJsrxBkV3kc
2 голосов
/ 10 января 2010

Если вы хотите сослаться на тот же объект, просто используйте 'self':

class A:
    def some_func(self):
        another_func(self)

Если вы хотите создать новый объект того же класса, просто сделайте это:

class A:
    def some_func(self):
        foo = A()

Если вы хотите иметь доступ к объекту класса metaclass (скорее всего, не к тому, что вам нужно), просто сделайте это:

class A:
    def some_func(self):
        another_func(A) # note that it reads A, not A()
1 голос
/ 10 января 2010

Если вы хотите сделать немного хакерской вещи, сделайте

class A(object):
    ...
some_func(A)

Если вы хотите сделать что-то более сложное, вы можете использовать метакласс. Метакласс отвечает за манипулирование объектом класса до его полного создания. Шаблон будет:

class AType(type):
    def __new__(meta, name, bases, dct):
        cls = super(AType, meta).__new__(meta, name, bases, dct)
        some_func(cls)
        return cls

class A(object):
    __metaclass__ = AType
    ...

type - метакласс по умолчанию. Экземпляры метаклассов являются классами, поэтому __new__ возвращает измененный экземпляр (в данном случае) A.

Подробнее о метаклассах см. http://docs.python.org/reference/datamodel.html#customizing-class-creation.

1 голос
/ 10 января 2010

Неа. Это работает в функции, потому что содержимое функции выполняется во время вызова. Но содержимое класса выполняется в определенное время, и в этот момент класс еще не существует.

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

class A(object):
    spam= 1

some_func(A)

A.eggs= 2

def _A_scramble(self):
    self.spam=self.eggs= 0
A.scramble= _A_scramble

Однако довольно необычно хотеть вызывать функцию класса в середине его собственного определения. Непонятно, что вы пытаетесь сделать, но, скорее всего, вам будет лучше с декораторами (или относительно новыми декораторами класса ).

1 голос
/ 10 января 2010

Нет способа сделать это в пределах класса, если только A не был определен как что-то сначала (а затем some_func (A) сделает что-то совершенно отличное от того, что вы ожидаете)

Если вы не проводите какую-либо проверку стека для добавления битов в класс, кажется странным, почему вы захотите это сделать. Почему бы просто:

class A:
    # do something
    pass

some_func(A)

То есть, запустите some_func на A после того, как это было сделано. В качестве альтернативы, вы можете использовать декоратор класса (синтаксис для него был добавлен в 2.6) или метакласс, если вы хотите как-то изменить класс А. Не могли бы вы уточнить ваш вариант использования?

0 голосов
/ 10 августа 2018

Если цель состоит в том, чтобы вызвать функцию some_func с классом в качестве аргумента, один из ответов - объявить some_func в качестве декоратора класса.Обратите внимание, что декоратор класса вызывается после инициализации класса.Классу, который оформляется как аргумент, будет передан класс.

def some_func(cls):
    # Do something
    print(f"The answer is {cls.x}")
    return cls # Don't forget to return the class

@some_func
class A:
    x = 1

Если вы хотите передать дополнительные аргументы some_func, вы должны вернуть функцию из декоратора:

def some_other_func(prefix, suffix):
    def inner(cls):
        print(f"{prefix} {cls.__name__} {suffix}")
        return cls
    return inner

@some_other_func("Hello", " and goodbye!")
class B:
    x = 2

Можно создавать декораторы классов, в результате чего они вызываются в обратном порядке, в котором они объявлены:

@some_func
@some_other_func("Hello", "and goodbye!")
class C:
    x = 42

Результат которого:

# Hello C and goodbye!
# The answer is 42
0 голосов
/ 10 января 2010

Можно ссылаться на имя класса внутри его тела (как в определениях методов), если он действительно находится в области видимости ... Что будет, если он определен на верхнем уровне. (В других случаях, вероятно, нет, из-за причуд в Python!).

Ибо на иллюстрации области видимости попробуйте создать экземпляр Foo:

class Foo(object):
    class Bar(object):
        def __init__(self):
            self.baz = Bar.baz
        baz = 15
    def __init__(self):
        self.bar = Foo.Bar()

(Он будет жаловаться на то, что глобальное имя 'Bar' не определено.)


Кроме того, что-то подсказывает мне, что вы можете захотеть взглянуть на методы класса: документы по функции classmethod (для использования в качестве декоратора), соответствующий вопрос SO . Редактировать: Хорошо, так что это предложение может вообще не подходить ... Просто первое, о чем я подумал, когда читал ваш вопрос, было что-то вроде альтернативных конструкторов и т.д. очистить от @classmethod странностей. : -)

0 голосов
/ 10 января 2010

Чего вы хотите достичь? Можно получить доступ к классу, чтобы настроить его определение с помощью метакласса, но это не рекомендуется.

Ваш пример кода может быть записан просто как:

class A(object):
    pass
some_func(A)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...