KL.
KL.

Reputation: 951

How to refer to the class from within it (like a recursive function)

For a recursive function we can do:

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

f(10)

However is there a way to do the following thing?

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

Upvotes: 57

Views: 63075

Answers (13)

OrenIshShalom
OrenIshShalom

Reputation: 7112

It seems most of the answers here are outdated. From python3.7:

from __future__ import annotations

Example:

$ cat rec.py
from __future__ import annotations
class MyList:
    def __init__(self,e):
        self.data = [e]
    def add(self, e):
        self.data.append(e)
        return self
    def score(self, other:MyList):
        return len([e
            for e in self.data
            if e in other.data])
print(MyList(8).add(3).add(4).score(MyList(4).add(9).add(3)))
$ python3.7 rec.py
2

Upvotes: 1

Maybe you could try calling __class__. Right now I'm writing a code that calls a class method from within the same class. It is working well so far.

I'm creating the class methods using something like:

@classmethod
    def my_class_method(cls):
       return None

And calling then by using:

x = __class__.my_class_method()

Upvotes: 4

Chin Pang Yau
Chin Pang Yau

Reputation: 659

If I understand your question correctly, you should be able to reference class A within class A by putting the type annotation in quotes. This is called forward reference.

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

See ref below

  1. https://github.com/python/mypy/issues/3661
  2. https://www.youtube.com/watch?v=AJsrxBkV3kc

Upvotes: 51

cdiggins
cdiggins

Reputation: 18203

If the goal is to call a function some_func with the class as an argument, one answer is to declare some_func as a class decorator. Note that the class decorator is called after the class is initialized. It will be passed the class that is being decorated as an argument.

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

If you want to pass additional arguments to some_func you have to return a function from the decorator:

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

Class decorators can be composed, which results in them being called in the reverse order they are declared:

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

The result of which is:

# Hello C and goodbye!
# The answer is 42

Upvotes: 2

Anurag Uniyal
Anurag Uniyal

Reputation: 88757

In Python you cannot reference the class in the class body, although in languages like Ruby you can do it.

In Python instead you can use a class decorator but that will be called once the class has initialized. Another way could be to use metaclass but it depends on what you are trying to achieve.

Upvotes: 21

Danny Roberts
Danny Roberts

Reputation: 3572

If you want to do just a little hacky thing do

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

If you want to do something more sophisticated you can use a metaclass. A metaclass is responsible for manipulating the class object before it gets fully created. A template would be:

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 is the default metaclass. Instances of metaclasses are classes so __new__ returns a modified instance of (in this case) A.

For more on metaclasses, see http://docs.python.org/reference/datamodel.html#customizing-class-creation.

Upvotes: 2

Mark Roddy
Mark Roddy

Reputation: 27936

You can't with the specific syntax you're describing due to the time at which they are evaluated. The reason the example function given works is that the call to f(i-1) within the function body is because the name resolution of f is not performed until the function is actually called. At this point f exists within the scope of execution since the function has already been evaluated. In the case of the class example, the reference to the class name is looked up during while the class definition is still being evaluated. As such, it does not yet exist in the local scope.

Alternatively, the desired behavior can be accomplished using a metaclass like such:

class MetaA(type):

    def __init__(cls):
        some_func(cls)

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

Using this approach you can perform arbitrary operations on the class object at the time that the class is evaluated.

Upvotes: 16

carl
carl

Reputation: 50554

If you want to refer to the same object, just use 'self':

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

If you want to create a new object of the same class, just do it:

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

If you want to have access to the metaclass class object (most likely not what you want), again, just do it:

class A:
    def some_func(self):
        another_func(A) # note that it reads A, not A()

Upvotes: 0

Michał Marczyk
Michał Marczyk

Reputation: 84341

It's ok to reference the name of the class inside its body (like inside method definitions) if it's actually in scope... Which it will be if it's defined at top level. (In other cases probably not, due to Python scoping quirks!).

For on illustration of the scoping gotcha, try to instantiate Foo:

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

(It's going to complain about the global name 'Bar' not being defined.)


Also, something tells me you may want to look into class methods: docs on the classmethod function (to be used as a decorator), a relevant SO question. Edit: Ok, so this suggestion may not be appropriate at all... It's just that the first thing I thought about when reading your question was stuff like alternative constructors etc. If something simpler suits your needs, steer clear of @classmethod weirdness. :-)

Upvotes: -1

Crast
Crast

Reputation: 16326

There isn't a way to do that within the class scope, not unless A was defined to be something else first (and then some_func(A) will do something entirely different from what you expect)

Unless you're doing some sort of stack inspection to add bits to the class, it seems odd why you'd want to do that. Why not just:

class A:
    # do something
    pass

some_func(A)

That is, run some_func on A after it's been made. Alternately, you could use a class decorator (syntax for it was added in 2.6) or metaclass if you wanted to modify class A somehow. Could you clarify your use case?

Upvotes: 2

bobince
bobince

Reputation: 536379

Nope. It works in a function because the function contents are executed at call-time. But the class contents are executed at define-time, at which point the class doesn't exist yet.

It's not normally a problem because you can hack further members into the class after defining it, so you can split up a class definition into multiple parts:

class A(object):
    spam= 1

some_func(A)

A.eggs= 2

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

It is, however, pretty unusual to want to call a function on the class in the middle of its own definition. It's not clear what you're trying to do, but chances are you'd be better off with decorators (or the relatively new class decorators).

Upvotes: 2

Tobu
Tobu

Reputation: 25426

What do you want to achieve? It's possible to access a class to tweak its definition using a metaclass, but it's not recommended.

Your code sample can be written simply as:

class A(object):
    pass
some_func(A)

Upvotes: 1

Ned Batchelder
Ned Batchelder

Reputation: 375574

Most code in the class will be inside method definitions, in which case you can simply use the name A.

Upvotes: -2

Related Questions