1

I am going to try my best to explain what I am trying to accomplish. I am trying to return an instance of a new class from a class instead of return self. Please refer to the comments in the example code.

class Test(object):
    def __init__(self, a):
        self.a = a

    def methoda(self):
        return len(self.a)


class SomeClass(object):
    def __init__(self, lol):
        self.lol = lol
        self.test = Test(self.lol)

    def __call__(self):
        return self.test # this isnt going to work


c = SomeClass('bar')
print(c) # trying to access the Test class attributes and methods  here
# so in the above example, i want to do
# print(c.a) # print bar
# print(c.length() # print 3

__repr__ and __str__ wouldnt work in this case because I am trying to get the Test class object back. Other things i have tried in SomeClass is having something like self.test = Test(self.lol), but that doesnt seem to quite do what I want.

How can I do this?

4
  • 1
    print(c())? You're having the object act as a function, so you'll need to call it. Commented Oct 30, 2019 at 18:43
  • AH! That was a simple enough slip. Thanks @Carcigenicate. Is there something I can do to access it more like a property instead of calling it? Commented Oct 30, 2019 at 18:44
  • 1
    You can just access it using c.test. You already have it as an attribute. Commented Oct 30, 2019 at 18:45
  • You ate changing defualt attribute access behavior so there is nothing clean about it but you could override getattr to return test attributes if they are not found on the calling object. Commented Oct 30, 2019 at 18:48

4 Answers 4

3

You can override the __getattr__ method of SomeClass so that attributes not defined in a SomeClass object can be delegated to self.test:

class Test(object):
    def __init__(self, a):
        self.a = a

    def methoda(self):
        return len(self.a)

class SomeClass(object):
    def __init__(self, lol):
        self.lol = lol
        self.test = Test(self.lol)

    def __getattr__(self, item):
        return getattr(self.test, item)

c = SomeClass('bar')
print(c.methoda())

This outputs:

3
Sign up to request clarification or add additional context in comments.

3 Comments

This is exactly what I am looking for. The only tiny issue that I am facing (and i didnt explain it properly) is that if a method in SomeClass modifies lol, like self.lol * 2, when I do print(c.a), it will return bar, and not barbar. Anyway how to address this? My guess is to move self.test = Test(self.lol) somewhere else, but not sure where
It sounds like you don't need the Test class at all, but simply a property-decorated method that returns self.lol * 2 then.
Actually I figured it out. Your answer really taught me about delegating to other classes. In my code, there is a lot of method chaining, so thats why i wanted to kind of separate out the different methods. Thanks for the awesome answer!
1

No quite convinced of the use case but you can use some proxy methods in SomeClass (use property whenever needed) to delegate the calls to the respective Test methods:

In [343]: class Test(object): 
     ...:     def __init__(self, a): 
     ...:         self.a = a 
     ...:  
     ...:     def methoda(self): 
     ...:         return len(self.a) 
     ...:  
     ...:  
     ...: class SomeClass(object): 
     ...:     def __init__(self, lol): 
     ...:         self.lol = lol 
     ...:         self.test = Test(self.lol) 
     ...:      
     ...:     @property 
     ...:     def a(self): 
     ...:         return self.test.a 
     ...:          
     ...:     def length(self): 
     ...:         return self.test.methoda() 
     ...:  
     ...:     def __call__(self): 
     ...:         return self.test 
     ...:                                                                                                                                                                                                   

In [344]: c = SomeClass('bar')                                                                                                                                                                              

In [345]: c.a                                                                                                                                                                                               
Out[345]: 'bar'

In [346]: c.length()                                                                                                                                                                                        
Out[346]: 3

Also note that, you can call the SomeClass instance i.e. c as you have the __call_- method defined, and returns the Test instance. That way you can get the values using:

In [347]: print(c())                                                                                                                                                                                        
<__main__.Test object at 0x7fba862fc3c8>

In [348]: print(c().a)                                                                                                                                                                                      
bar

In [349]: print(c().methoda())                                                                                                                                                                              
3

Comments

1
  • For this you need to understand when __init__ is called and when __call__ is called.
class foo(object):
    def __init__(self, lol):
        print('__init__ called')
    def __call__(self):
        print('__call__ called')

ob = foo('lol')
print(ob())
_ = foo('lol')()
  • this will output:
__init__ called # ob = foo('lol')
__call__ called # print(ob())
__init__ called # _ = foo('lol')()
__call__ called # _ = foo('lol')()
  • __init__ is called when class is instantiate. __call__ is called when you call that object of class as function.

  • To access Test class in your case you can do following.

c = SomeClass('bar')
test_c = c()
print(c.a)
print(c.methoda)

Comments

0

What you are looking for is the __new__ special method. It allows to build from scratch the object created from the class:

class SomeClass(object):
    def __new__(cls, lol):
        obj = Test(lol)
        return obj

You can then use it:

>>> c = SomeClass('bar')
>>> c
<__main__.Test object at 0x03F87750>
>>> c.a
'bar'
>>> c.methoda()
3

But you should not unless you make Test a subclass of SomeClass. It is at least uncommon that SomeClass('bar') returns an object that is not a SomeClass, and you could be burned later because of that...

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.