FBidu
FBidu

Reputation: 1012

How to pass a callable to a class so that it can access instance attributes in Python?

Suppose I have this class:

class ClassA:
    def __init__(self, callable):
        self.name = "Hey!"
        self.callable = callable

    def call(self):
        self.callable()

And I want to pass into callable a function that can access name:

def function_a():
    print(f"{self.name}")

So that this

a = ClassA(function_a)
a.call()

Yields this:

Hey!

How should I go about this? Metaclassing? Class decoration? Or should I just overhaul my architecture so that I don't need to do such a thing?

Edit with a more clear example

The former example seems like I'm just trying to access that attribute, this is not the case. In a more sophisticated demo, consider this:

class ClassA:
    def __init__(self, selection):
        self.numbers = [randint(1, 100) for _ in range(10)]
        self.selection = selection

    def select(self):
        return self.selection(self.numbers)

And then suppose I want the caller to be able to provide different strategies for selecting those numbers, like

a = ClassA(selection_strategy)
a.select()

In that architecture, selection_strategy is a callable defined outside of the class that needs to somehow have access to that object attributes.

Aaaaand just as I was writing this, I realized that what I want is actually really simple, I just have to make selection_strategy accept the arguments I want from the class and call it within select. I'm sorry, SO, I've been working for a few hours and this totally slipped by me.

Thanks!

Upvotes: 0

Views: 2136

Answers (5)

Igor Rivin
Igor Rivin

Reputation: 4864

Since it is not clear what the point of any of this is, it is hard to know what answer will work. However, other than the obvious idea of the "callable" taking an argument, the simplest thing is to define a callable class

Class callable(object):

def __init__(self, otherclass):
   self.name = otherclass.name

def _call__(self):
   print(self.name)

then instantiate this with

foo = callable(ClassAInstance)

Upvotes: 1

tdelaney
tdelaney

Reputation: 77407

The callable can't officially be a method because methods must be defined on the class object itself. But does it really have to be a method? You could just define the callable as a regular function taking a single parameter that just happens to be an instance object's self reference. Namespace resolution is different - you can't use class level variables - but if that's not a problem, this will work:

class ClassA:
    def __init__(self, callable):
        self.name = "Hey!"
        self.callable = callable

    def call(self):
        self.callable(self)

def function_a(self):
    print(f"{self.name}")

a = ClassA(function_a)
a.call()

Upvotes: 2

Joran Beasley
Joran Beasley

Reputation: 114108

this seems like a terrible idea but here you go

>>> def hello():
...     print 'hello: '+name
...
>>> hello()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in hello
NameError: global name 'name' is not defined
>>> exec hello.func_code in {'name':'bob'}
hello: bob
>>>

Upvotes: 1

Krishnan Shankar
Krishnan Shankar

Reputation: 940

If you want the function to be able to use self, you must make it a method, like this:

class ClassA:
    def __init__(self):
        self.name = "Hey!"
        self.callable = self.function_a

    def call(self):
        self.callable()

    def function_a(self):
        print(f"{self.name}")

Or, you can pass the name as a parameter:

class ClassA:
    def __init__(self):
        self.name = "Hey!"
        self.callable = self.function_a

    def call(self):
        self.callable(self.name)

def function_a(name):
    print(f"{name}")

a = ClassA(function_a)
a.call()

Hope this helps!

Upvotes: 0

Jan
Jan

Reputation: 43199

I wonder if this is of any real use, but you could use

    ...
    def call(self):
        self.callable(self)

def function_a(cls):
    print(f"{cls.name}")

a = ClassA(function_a)
a.call()
# Hey!

Upvotes: 1

Related Questions