简体   繁体   中英

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!

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!

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
>>>

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)

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()

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!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM