views:

142

answers:

5

Hi,

I am traditionally a Perl and C++ programmer, so apologies in advance if I am misunderstanding something trivial about Python!

I would like to create a reference to a reference. Huh? Ok. All objects in Python are actually references to the real object. So, how do I create a reference to this reference?

Why do I need/want this? I am overriding sys.stdout and sys.stderr to create a logging library. I would like a (second-level) reference to sys.stdout.

If I could create a reference to a reference, then I could create a generic logger class where the init function receives a reference to a file handle reference that will be overrided, e.g., sys.stdout or sys.stderr. Currently, I must hard-code both values.

Cheers, Kevin

A: 

This can't be done. Pass the desired attribute as a string and use getattr() and setattr().

Ignacio Vazquez-Abrams
+1  A: 

You can't create references to references in python. You can however override the stderr and stdout files with custom classes with write etc methods to allow your own logging systems:

import sys

class MyLogger:
    def __init__(self, f):
        self.f = f

    def __getattr__(self, name):
        # forward e.g. flush() calls to the original file
        return getattr(self.f, name)

    def write(self, data):
        # log the data here!
        # ...

        # And write to the original file
        self.f.write(data)

sys.stdout = MyLogger(sys.stdout)
sys.stderr = MyLogger(sys.stderr)
David Morrissey
The face that [sys.stdout = MyLogger(sys.stdout)] cannot be made generic is proof that Python is missing something. Still, I like everything else I see.Fortunately, there are only two file handles to override. I can 'afford' to repeat twice =)Thanks for the advice. This solution most closely fits my original need.
KCArpe
Oh, also. Great tip with \__getattr__. I will add.
KCArpe
I've overriden the `stdout` and `stderr`, but it is possible to replace them with any object/class which has the method `write` (such as an alternative file, so `sys.stdout = open('mylog', 'wb')` will work.) I don't think it's limited at all - it's just a different language with a different philosophy behind it - references to objects I think go against the python philosophy (http://www.python.org/dev/peps/pep-0020/), though I'm sure it fits with perl's "expressive" model which has it's own advantages and disadvantages :-P
David Morrissey
+6  A: 

Easier done than said:

ostream = sys.stdout
print >> ostream, 'hi mom!'
ostream = sys.stderr
print >> ostream, 'hi mom!'
ostream = open('mylog.txt', 'a')
...

And look at the standard logging module when you have some more Python under your belt.

This answer was based on the presumption, from the level of the question, of what was really needed. The concept of a reference to a reference is not needed in Python, you can multiplex through a list or a dict if you want:

outputs = [sys.stderr, my_open_file_object_which_is_really_a_reference]
print >> outputs[0], 'hi dad!'
outputs = {'terminal': sys.stderr, 'logfile': file_object}
print >> outputs['logfile'], 'goodbye world!'

and so on.

msw
+1 for mentioning the standard logging module.
Michael Aaron Safyan
A: 

Firstly, there is already a logging module, so you probably should just use that. Secondly, while there is no such thing as a reference to a reference, you can achieve that indirection via a wrapper or function. For example, if you were to create a getter and setter function for assigning the object, like:

 class StdOutWrapper(object):
      def __init__(self):
          self.original = sys.stdout

      @property
      def value(self):
          return sys.stdout

      @value.setter
      def value(self,val):
          sys.stdout = val

You could then pass an instance of this object to your logger to assign/dereference sys.stdout.

Michael Aaron Safyan
The trouble with this solution is that I must hard-code sys.stdout. This class cannot be created generically.
KCArpe
@KCArpe, yeah, that is correct. I was trying to save coding for whatever made use of the wrapper... the wrapper, itself, would have to be written both for sys.stdout and sys.stdin.
Michael Aaron Safyan
A: 

As the other answers say, there is no true "references of references" in python, but there are ways of getting nearly the same effect:

>>> reference1 = "Some Data"
>>> reference2 = (reference1,)
>>> def f(data):
    print data

>>> f(reference2)
('Some Data',)
>>> f(*reference2)
Some Data
dln385