Python Forum
how to get around recursive method call
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
how to get around recursive method call
#1
i did this:
...
def __getattr__(self,*args):
    print(f'attribute {args[0]!r}')
    return getattr(self,*args)
...
and got the recursion error. i left "self." off the call, but it got many recursion errors, anyway. rethinking about it, it would have to come back here. is there a way around this?
Tradition is peer pressure from dead people

What do you call someone who speaks three languages? Trilingual. Two languages? Bilingual. One language? American.
Reply
#2
Do you mean to use __getattribute__
class YourObject:
    def __getattribute__(self, name):
        print(f'attribute {name}')
        return object.__getattribute__(self, name)


your_instance = YourObject()
your_instance.this = 'This'
print(your_instance.this)
Output:
attribute 'this' This
Note: __getattr__ is called when there is no attribute
https://docs.python.org/3/reference/data..._getattr__ Wrote:object.__getattr__(self, name)
Called when the default attribute access fails with an AttributeError (either __getattribute__() raises an AttributeError because name is not an instance attribute or an attribute in the class tree for self; or __get__() of a name property raises AttributeError). This method should either return the (computed) attribute value or raise an AttributeError exception.

Note that if the attribute is found through the normal mechanism, __getattr__() is not called. (This is an intentional asymmetry between __getattr__() and __setattr__().) This is done both for efficiency reasons and because otherwise __getattr__() would have no way to access other attributes of the instance. Note that at least for instance variables, you can fake total control by not inserting any values in the instance attribute dictionary (but instead inserting them in another object). See the __getattribute__() method below for a way to actually get total control over attribute access.
Reply
#3
i tried to juggle things around with a __getattr__() method and finally eliminated the recursion. but it still ran a finite 2 times. no idea why that happens. but i will try __getattribute__() and see if i can make that work.

the class is emulating open() and is intercepting close(). it needs to pass everything else straight through. it's a no-compress version of the class described here.

i know you want to see my code. please focus on this issue in this thread.

this it class topen in file "topen.py":
import io,os,sys,time

_separator = '_'

class topen(io.IOBase):
    """Class for opening a file with temporary output name and automatic rename on close if write."""
    # this class also adds the name= keyword argument (no default)
    # name is require by positional args or by keyword args
    # this class is like ztopen but without compression

    def __init__(self,*args,**kwargs):

        if args:
            args = list(args)
            if 'name' in kwargs:
                raise TypeError('name given twice')
            fname = args.pop(0)
            if args:
                if 'mode' in kwargs:
                    raise TypeError('mode given twice')
                modes = args.pop(0)
            elif 'mode' in kwargs:
                modes = kwargs.pop('mode')
            else:
                raise TypeError('mode not given')
        elif 'name' in kwargs:
            fname = kwargs.pop('name')
            modes = kwargs.pop('mode','r')
        else:
            raise TypeError('name not given')

        if 'x' in modes and isinstance(fname,(str,bytes)) and os.path.exists(fname):
            raise TypeError(f'{fname!r} refers to a name that already exists')

        fname = os.fspath(fname)
        if isinstance(fname,bytes):
            fname = ''.join(chr(x)for x in fname)
        if isinstance(modes,bytes):
            modes = ''.join(chr(x)for x in modes)

        if isinstance(fname,str) and '+' not in modes:
            rname = 'x' in modes or 'w' in modes
            oname = fname+_separator+str(int(time.time()*3906250)) if rname else fname
        elif isinstance(fname,int):
            rname = False
            oname = fname
        else:
            raise TypeError()

        print(f'opening file {oname!r} with mode {modes!r}',file=sys.stderr,flush=1)
        ofile = open(oname,modes,*args,**kwargs)
        if ofile is None:
            raise(f'failed to open file {oname!r}')

        self.rname = rname
        self.ofile = ofile
        self.oname = oname
        self.fname = fname

    def close(self):
        if self.rname:
            print(f'close {self.oname!r} and rename it to {self.fname!r}',file=sys.stderr,flush=1)
            self.ofile.close()
            return os.rename(self.oname,self.fname)
        else:
            print(f'close {self.oname!r}',file=sys.stderr,flush=1)
            return self.ofile.close()

    def __getattr__(self,*args):
        print(f'__getattr__ for {args!r}',file=sys.stderr,flush=1)
        return getattr(self.ofile,*args)
here is the test script in file "trytopen.py":
from topen import topen
f=topen('foo','w')
print('bar',file=f)
f.close()
i plan to try with with in test 2 or 3.

here is the output i get:
Output:
lt2a/phil /home/phil 188> py trytopen.py opening file 'foo_6224487216638762' with mode 'w' __getattr__ for ('write',) __getattr__ for ('write',) close 'foo_6224487216638762' and rename it to 'foo' __getattr__ for ('__IOBase_closed',) close 'foo_6224487216638762' and rename it to 'foo' lt2a/phil /home/phil 189>
Tradition is peer pressure from dead people

What do you call someone who speaks three languages? Trilingual. Two languages? Bilingual. One language? American.
Reply
#4
i added some more points of output and made repeated calls in a loop. now it shows things that are more strange. the extra 2nd call of close() happens in the next loop cycle or for the last one, after the script reached its end.

the code has been updated.

topen.py:
# -*- coding: utf-8 -*-
import io,os,sys,time

def _sep():
    return '_'+str(int(time.time()*3906250))

class topen(io.IOBase):
    """Class for opening a file with temporary output name and automatic rename on close if write."""
    # name is require by positional args or by keyword args
    # this class is like ztopen but without compression

    def __init__(self,*args,**kwargs):

        if args:
            args = list(args)
            fname = args.pop(0)
            if args:
                modes = args.pop(0)
            else:
                modes = kwargs.pop('mode',None) 
        else:
            fname = kwargs.pop('file',None)
            modes = kwargs.pop('mode',None)

        if fname is None:
            raise('file is missing')
        if modes is None:
            raise('mode is missing')

        if 'x' in modes and isinstance(fname,(str,bytes)) and os.path.exists(fname):
            raise TypeError(f'{fname!r} refers to a name that already exists')

        fname = os.fspath(fname)
        if isinstance(fname,bytes):
            fname = ''.join(chr(x)for x in fname)
        if isinstance(modes,bytes):
            modes = ''.join(chr(x)for x in modes)

        if ('x' in modes or 'w' in modes) and isinstance(fname,str):
            rname = 1
            oname = fname+_sep()
        else:
            rname = 0
            oname = fname

        args = [oname,modes]+list(args)

        print(f'CALLING open(*{args!r},**{kwargs!r}',file=sys.stderr,flush=1)
        ofile = open(*args,**kwargs)
        if ofile is None:
            raise(f'failed to open file {oname!r}')

        self.rname = rname
        self.ofile = ofile
        self.oname = oname
        self.fname = fname

    def close(self):
        if self.rname:
            print(f'close {self.oname!r} and rename it to {self.fname!r}',file=sys.stderr,flush=1)
            self.ofile.close()
            return os.rename(self.oname,self.fname)
        else:
            print(f'close {self.oname!r}',file=sys.stderr,flush=1)
            return self.ofile.close()

    def __getattr__(self,*args):
#        print(f'__getattr__ for {args!r}',file=sys.stderr,flush=1)
        return getattr(self.ofile,*args)
trytopen.py:
# -*- coding: utf-8 -*-
from sys import stderr

from topen import topen
for n in range(4):
    print(f'--------------------------------------------------- start{n}',file=stderr,flush=1)
    f=topen(f'foo{n}','w')
    print(f'bar{n}',file=f)
    f.close()
    print(f'---------------------------------------------------- done{n}',file=stderr,flush=1)

print('ALL DONE NOW ENDED',file=stderr,flush=1)
topen.out:
Output:
--------------------------------------------------- start0 CALLING open(*['foo0_6224542024360958', 'w'],**{} close 'foo0_6224542024360958' and rename it to 'foo0' ---------------------------------------------------- done0 --------------------------------------------------- start1 CALLING open(*['foo1_6224542024362419', 'w'],**{} close 'foo0_6224542024360958' and rename it to 'foo0' close 'foo1_6224542024362419' and rename it to 'foo1' ---------------------------------------------------- done1 --------------------------------------------------- start2 CALLING open(*['foo2_6224542024363304', 'w'],**{} close 'foo1_6224542024362419' and rename it to 'foo1' close 'foo2_6224542024363304' and rename it to 'foo2' ---------------------------------------------------- done2 --------------------------------------------------- start3 CALLING open(*['foo3_6224542024364380', 'w'],**{} close 'foo2_6224542024363304' and rename it to 'foo2' close 'foo3_6224542024364380' and rename it to 'foo3' ---------------------------------------------------- done3 ALL DONE NOW ENDED close 'foo3_6224542024364380' and rename it to 'foo3'
Tradition is peer pressure from dead people

What do you call someone who speaks three languages? Trilingual. Two languages? Bilingual. One language? American.
Reply
#5
how do i get the attribute value in def __getattribute__(self,attr): when it would be calling itself? isn't that the infinite recursion problem?
Tradition is peer pressure from dead people

What do you call someone who speaks three languages? Trilingual. Two languages? Bilingual. One language? American.
Reply
#6
(Jun-30-2020, 01:57 PM)Skaperen Wrote: how do i get the attribute value in def __getattribute__(self,attr): when it would be calling itself? isn't that the infinite recursion problem?
That is what is shown in my reply
https://python-forum.io/Thread-how-to-ge...#pid119313
by using objects __getattribute__ method
Reply
#7
your reply code shows it getting the attribute of something else using the name object. but i don't have that. i need to have it return the attribute of itself. thus, i need to find a way to get the attribute of itself without calling itself.

i want to have object A behave like object B except for intended differences. i implement the methods intended to be different. for the remaining methods, i don't even know what all they are, or will be in the future, so i implement __getattribute__ to pass references. that method ends up referencing itself to reference object B. because it references itself, it ends up calling itself.

def __getattribute__(self,attr):
    print(f'getting attribute {attr!r}',flush=True) # so i can see what is happening
    return self.objectb.__getattribute__(attr)
output will be one line for the first attribute then a few hundred for 'objectb', hence the recursion loop.

even your code will have this problem since it gets attributes inside itself.
Tradition is peer pressure from dead people

What do you call someone who speaks three languages? Trilingual. Two languages? Bilingual. One language? American.
Reply
#8
import io

class YourObject(io.IOBase):
    def __init__(self):
        self.value = 'this value'

    def some_method(self):
        return 'some_method'
    
    def __getattribute__(self, name):
        print(f'attribute {name}')
        return object.__getattribute__(self, name)
 
 
your_instance = YourObject()

print(your_instance.value)
print(your_instance.some_method())
print('finished with YourObject')
Output:
attribute value this value attribute some_method some_method finished with YourObject attribute closed attribute __IOBase_closed attribute close attribute __IOBase_closed attribute flush attribute __IOBase_closed
Accessing attributes does not happen recursively in the above code
The following are called when the class is done with
Output:
attribute closed attribute __IOBase_closed attribute close attribute __IOBase_closed attribute flush attribute __IOBase_closed
Reply
#9
when object is self?
Tradition is peer pressure from dead people

What do you call someone who speaks three languages? Trilingual. Two languages? Bilingual. One language? American.
Reply
#10
(Jul-01-2020, 08:16 PM)Skaperen Wrote: when object is self?
Think Huh

call the baseclass method __getattribute__
class BaseClass(object):
    def __getattribute__(self, name):
        print('Calling object')
        return object.__getattribute__(self, name)


class MiddleClass(BaseClass):
    def __getattribute__(self, name):
        print('Calling BaseClass')
        return BaseClass.__getattribute__(self, name)

class SuperClass(MiddleClass):
    def __getattribute__(self, name):
        print('Using Super to Call MiddleClass')
        return super().__getattribute__(name)

my_class = SuperClass()
my_class.value = 'Spam'
print(my_class.value)
Output:
Using Super to Call MiddleClass Calling BaseClass Calling object Spam
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  method call help sollarriiii 6 1,079 Feb-21-2023, 03:19 AM
Last Post: noisefloor
  Combine Two Recursive Functions To Create One Recursive Selection Sort Function Jeremy7 12 7,199 Jan-17-2021, 03:02 AM
Last Post: Jeremy7
  list call problem in generator function using iteration and recursive calls postta 1 1,863 Oct-24-2020, 09:33 PM
Last Post: bowlofred
  Return boolean from recursive class method medatib531 6 3,409 Jul-13-2020, 04:27 AM
Last Post: medatib531
  How to call COM-method using comtypes jespersahner 0 2,388 Nov-15-2019, 12:54 PM
Last Post: jespersahner
  Polymorphism not working with a call to a abstract method colt 3 2,277 Nov-04-2019, 11:04 PM
Last Post: colt
  How to Call a method of class having no argument dataplumber 7 6,322 Oct-31-2019, 01:52 PM
Last Post: dataplumber
  Call method from another method within a class anteboy65 3 7,353 Sep-11-2019, 08:40 PM
Last Post: Larz60+
  What is the use of call method and when to use it? everyday1 1 3,269 Jul-14-2019, 01:02 PM
Last Post: ichabod801
  I'm trying to figure out whether this is a method or function call 357mag 2 2,380 Jul-04-2019, 01:43 AM
Last Post: ichabod801

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020