print doesn't respect file inheritance?

B

bukzor

I was trying to change the behaviour of print (tee all output to a
temp file) by inheriting from file and overwriting sys.stdout, but it
looks like print uses C-level stuff to do its writes which bypasses
the python object/inhertiance system. It looks like I need to use
composition instead of inheritance, but thought this was strange
enough to note.

$python -V
Python 2.5

"""A short demo script"""
class notafile(file):
def __init__(self, *args, **kwargs):
readonly = ['closed', '__class__', 'encoding', 'mode', 'name',
'newlines', 'softspace']
file.__init__(self, *args, **kwargs)
for attr in dir(file):
if attr in readonly: continue
setattr(self, attr, None)


def main():
n = notafile('/dev/stdout', "w")
print vars(n)

import sys
sys.stdout = n
print "Testing: 1, 2, 3..."


output:
{'__str__': None, 'xreadlines': None, 'readlines': None, 'flush':
None, 'close': None, 'seek': None, '__init__': None, '__setattr__':
None, '__reduce_ex__': None, '__new__': None, 'readinto': None,
'next': None, 'write': None, '__doc__': None, 'isatty': None,
'truncate': None, 'read': None, '__reduce__': None,
'__getattribute__': None, '__iter__': None, 'readline': None,
'fileno': None, 'writelines': None, 'tell': None, '__delattr__': None,
'__repr__': None, '__hash__': None}
Testing: 1, 2, 3...
 
T

Terry Reedy

bukzor said:
I was trying to change the behaviour of print (tee all output to a
temp file) by inheriting from file and overwriting sys.stdout, but it
looks like print uses C-level stuff to do its writes which bypasses
the python object/inhertiance system. It looks like I need to use
composition instead of inheritance, but thought this was strange
enough to note.

$python -V
Python 2.5

"""A short demo script"""
class notafile(file):
def __init__(self, *args, **kwargs):
readonly = ['closed', '__class__', 'encoding', 'mode', 'name',
'newlines', 'softspace']
file.__init__(self, *args, **kwargs)
for attr in dir(file):
if attr in readonly: continue
setattr(self, attr, None)

Drop the __init__ and give notafile a .write method.
Composition version inheritance is not the the real issue.
def write(s):
print(s)
print(s)
testing
testing

Now change nf.write to put the copy where you want it.

tjr
 
D

D'Arcy J.M. Cain

Re-binds the name 'sys.stdout' to the object already referenced by the
name 'n'. No objects are changed by this; only bindings of names to
objects.

I do agree that the object formerly known as sys.stdout hasn't changed.
Doesn't rely at all on the name 'sys.stdout', so isn't affected by all
the binding of names above.

Hmm. Are you saying that the following doesn't work?

$ python$ cat test
test message
In other words, you can't change the object used by the 'print'
statement only by re-binding names (which is *all* that is done by the
'=' operator).

Apparently I can.
You can, however, specify which file 'print' should use
<URL:http://www.python.org/doc/ref/print.html>.

Which contains this statement.

"Standard output is defined as the file object named stdout in the
built-in module sys."

I suppose that there might be some ambiguity there but the proof, as
they say, is in the pudding.
 
L

Lie

I was trying to change the behaviour of print (tee all output to a
temp file) by inheriting from file and overwriting sys.stdout, but it
looks like print uses C-level stuff  to do its writes which bypasses
the python object/inhertiance system. It looks like I need to use
composition instead of inheritance, but thought this was strange
enough to note.

$python -V
Python 2.5

"""A short demo script"""
class notafile(file):
    def __init__(self, *args, **kwargs):
        readonly = ['closed', '__class__', 'encoding', 'mode', 'name',
'newlines', 'softspace']
        file.__init__(self, *args, **kwargs)
        for attr in dir(file):
            if attr in readonly: continue
            setattr(self, attr, None)

def main():
    n = notafile('/dev/stdout', "w")
    print vars(n)

    import sys
    sys.stdout = n
    print "Testing: 1, 2, 3..."

output:
{'__str__': None, 'xreadlines': None, 'readlines': None, 'flush':
None, 'close': None, 'seek': None, '__init__': None, '__setattr__':
None, '__reduce_ex__': None, '__new__': None, 'readinto': None,
'next': None, 'write': None, '__doc__': None, 'isatty': None,
'truncate': None, 'read': None, '__reduce__': None,
'__getattribute__': None, '__iter__': None, 'readline': None,
'fileno': None, 'writelines': None, 'tell': None, '__delattr__': None,
'__repr__': None, '__hash__': None}
Testing: 1, 2, 3...

Use this:

class fakefile(object):
def __init__(self, writeto, transformer):
self.target = writeto
self.transform = transformer
def write(self, s):
s = self.transform(s)
self.target.write(s)
sys.stdout = fakefile(sys.stdout, lambda s: '"' + s + '"')

Inheriting from file is not the best way to do it since there is a
requirement that child class' interface must be compatible with the
parent class' interface, since the file-like object you're creating
must have extremely different interface than regular file, the best
way is to use Duck Typing, i.e. write a class that have .write()
method.
 
B

bukzor

I do agree that the object formerly known as sys.stdout hasn't changed.



Hmm.  Are you saying that the following doesn't work?

$ python>>> f = open("test", "w")

$ cat test
test message


Apparently I can.


Which contains this statement.

"Standard output is defined as the file object named stdout in the
built-in module sys."

I suppose that there might be some ambiguity there but the proof, as
they say, is in the pudding.

Thanks for backing me up.

Nobody here thinks it's strange that print uses *none* of the
attributes or methods of sys.stdout to do its job? The implementation
seems to bypass the whole python system and use C-level FILE* pointers
directly instead.
 
E

Ethan Furman

bukzor said:
I was trying to change the behaviour of print (tee all output to a
temp file) by inheriting from file and overwriting sys.stdout, but it
looks like print uses C-level stuff to do its writes which bypasses
the python object/inhertiance system. It looks like I need to use
composition instead of inheritance, but thought this was strange
enough to note.

$python -V
Python 2.5

"""A short demo script"""
class notafile(file):
def __init__(self, *args, **kwargs):
readonly = ['closed', '__class__', 'encoding', 'mode', 'name',
'newlines', 'softspace']
file.__init__(self, *args, **kwargs)
for attr in dir(file):
if attr in readonly: continue
setattr(self, attr, None)


def main():
n = notafile('/dev/stdout', "w")
print vars(n)

import sys
sys.stdout = n
print "Testing: 1, 2, 3..."


output:
{'__str__': None, 'xreadlines': None, 'readlines': None, 'flush':
None, 'close': None, 'seek': None, '__init__': None, '__setattr__':
None, '__reduce_ex__': None, '__new__': None, 'readinto': None,
'next': None, 'write': None, '__doc__': None, 'isatty': None,
'truncate': None, 'read': None, '__reduce__': None,
'__getattribute__': None, '__iter__': None, 'readline': None,
'fileno': None, 'writelines': None, 'tell': None, '__delattr__': None,
'__repr__': None, '__hash__': None}
Testing: 1, 2, 3...

I tried the code (on Windows, so had to change /dev/stdout to
/temp/notafile.txt) and it worked just fine. Perhaps the issue is that
n is being set to /dev/stdout instead of some other file so no
difference is apparent?

In other words, you're assigning stdout to stdout.

~Ethan~
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,774
Messages
2,569,596
Members
45,144
Latest member
KetoBaseReviews
Top