Posts: 4,647
Threads: 1,494
Joined: Sep 2016
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.
Posts: 2,168
Threads: 35
Joined: Sep 2016
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.
Posts: 4,647
Threads: 1,494
Joined: Sep 2016
Jun-29-2020, 09:59 PM
(This post was last modified: Jun-29-2020, 10:27 PM by Skaperen.)
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.
Posts: 4,647
Threads: 1,494
Joined: Sep 2016
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.
Posts: 4,647
Threads: 1,494
Joined: Sep 2016
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.
Posts: 2,168
Threads: 35
Joined: Sep 2016
(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
Posts: 4,647
Threads: 1,494
Joined: Sep 2016
Jun-30-2020, 10:44 PM
(This post was last modified: Jun-30-2020, 11:01 PM by Skaperen.)
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.
Posts: 2,168
Threads: 35
Joined: Sep 2016
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
Posts: 4,647
Threads: 1,494
Joined: Sep 2016
Tradition is peer pressure from dead people
What do you call someone who speaks three languages? Trilingual. Two languages? Bilingual. One language? American.
Posts: 2,168
Threads: 35
Joined: Sep 2016
Jul-01-2020, 08:40 PM
(This post was last modified: Jul-01-2020, 09:01 PM by Yoriz.)
(Jul-01-2020, 08:16 PM)Skaperen Wrote: when object is self?
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
|