By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
440,345 Members | 1,758 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 440,345 IT Pros & Developers. It's quick & easy.

"Magic method" in python

Xx r3negade
P: 39
Is there any way to do this in python? Basically, a default action that an object performs when a nonexistent method or member is accessed.

Edit: Nevermind, I found __getattr__(). Feel free to delete.
Mar 28 '09 #1
Share this Question
Share on Google+
7 Replies


bvdet
Expert Mod 2.5K+
P: 2,851
I was going to post an example! I'll post it anyway.
Expand|Select|Wrap|Line Numbers
  1. class Vector(Point):
  2.  
  3.     def __init__(self, x=0.0, y=0.0, z=0.0):
  4.         Point.__init__(self, x, y, z)
  5.  
  6.     def __getattr__(self, name):
  7.         if not self.__dict__.has_key(name):
  8.             print "The attribute or method does not exist."
Example:
Expand|Select|Wrap|Line Numbers
  1. >>> p = Vector(1,2,3)
  2. >>> p.z
  3. 3.0
  4. >>> p.dist
  5. <bound method Vector.dist of Point(1.000000, 2.000000, 3.000000)>
  6. >>> p.dd
  7. The attribute or method does not exist.
  8. >>> 
Someone else may have the same question.

-BV
Mar 28 '09 #2

Xx r3negade
P: 39
Is there any way to get the arguments that were passed, too?

Something like this:
Expand|Select|Wrap|Line Numbers
  1. def __getattr__(name, *args):
  2.     # whatever 
  3.  
Except that obviously doesn't work...
Mar 28 '09 #3

bvdet
Expert Mod 2.5K+
P: 2,851
There is probably a better way.
Expand|Select|Wrap|Line Numbers
  1. class Vector(Point):
  2.  
  3.     def f(self, *args):
  4.         return args
  5.  
  6.     def __init__(self, x=0.0, y=0.0, z=0.0):
  7.         Point.__init__(self, x, y, z)
  8.  
  9.     def __getattr__(self, name, *args):
  10.         if not self.__dict__.has_key(name):
  11.             print "The attribute or method does not exist."
  12.             return self.f
  13.         return self.__dict__[name]
Expand|Select|Wrap|Line Numbers
  1. >>> p1 = Vector(1,2,3)
  2. >>> p1.x
  3. 1.0
  4. >>> p1.d
  5. The attribute or method does not exist.
  6. <bound method Vector.f of Point(1.000000, 2.000000, 3.000000)>
  7. >>> p1.d()
  8. The attribute or method does not exist.
  9. ()
  10. >>> p1.d('a', 'b')
  11. The attribute or method does not exist.
  12. ('a', 'b')
  13. >>> 
Mar 28 '09 #4

Xx r3negade
P: 39
Alright, now I've just dug myself into a deeper hole.

What I'm basically trying to do is this:

I have a base class, called FileSystem. I then have two derived classes called DiskFileSystem and VirtualFileSystem. Being derived classes, they have common methods like write(), read(), etc.

Say I want to write the same contents to the same file in a VirtualFileSystem object and a DiskFileSystem object. Instead of doing this:
Expand|Select|Wrap|Line Numbers
  1. vfs1 = VirtualFileSystem()
  2. dfs1 = DiskFileSystem()
  3. vfs1.write("/some/path/to/file", "testing")
  4. dfs1.write("/some/path/to/file", "testing")
  5.  
I want to do this....

Expand|Select|Wrap|Line Numbers
  1. vfs1 = VirtualFileSystem()
  2. dfs1 = DiskFileSystem()
  3. sfs = SuperFileSystem([vfs1, dfs1])
  4. sfs.write("/some/path/to/file", "testing")
  5.  
As you may have guessed, the "SuperFileSystem" class is what I'm currently trying to work on. I just don't know a good, "non-hackish" way to do this.
It's possible to do using __getattr__. When the called method isn't found in SuperFileSystem, SuperFileSystem will go though the list of FileSystem objects (the list passed to SuperFileSystem at its initialization), and execute the called function in each FileSystem object using getattr(). But it seems pretty unintuitive and un-OO. Any suggestions on how to clean this up?
Mar 29 '09 #5

bvdet
Expert Mod 2.5K+
P: 2,851
Almost anything is possible. A suggestion:
Expand|Select|Wrap|Line Numbers
  1. class FileSystem(object):
  2.     def __init__(self):
  3.         pass
  4.  
  5. class DiskFileSystem(FileSystem):
  6.     def __init__(self):
  7.         super(DiskFileSystem, self).__init__()
  8.  
  9.     def write(self, fn):
  10.         print "Writing to DiskFileSystem, file %s" % fn
  11.  
  12. class VirtualFileSystem(FileSystem):
  13.     def __init__(self):
  14.         super(VirtualFileSystem, self).__init__()
  15.  
  16.     def write(self, fn):
  17.         print "Writing to VirtualFileSystem, file %s" % fn
  18.  
  19.  
  20. class SuperFileSystem(object):
  21.     def __init__(self, *args):
  22.         self.args = args
  23.         pass
  24.  
  25.     def write(self, path, fn):
  26.         for arg in self.args:
  27.             arg.write('/'.join([path, fn]))
  28.  
  29. vfs1 = VirtualFileSystem()
  30. dfs1 = DiskFileSystem()
  31. sfs = SuperFileSystem(vfs1, dfs1)
  32. sfs.write("/some/path/to/file", "testing")
Output:
>>> Writing to VirtualFileSystem, file /some/path/to/file/testing
Writing to DiskFileSystem, file /some/path/to/file/testing
>>>
Mar 29 '09 #6

Xx r3negade
P: 39
If I followed your example, I would have to do this:
Expand|Select|Wrap|Line Numbers
  1.      def write(self, path, fn):
  2.          for arg in self.args:
  3.              arg.write('/'.join([path, fn]))
  4.  
over and over again for a bunch of different methods.

Here is basically how I ended up doing this:

Expand|Select|Wrap|Line Numbers
  1. class Parent(object):
  2.     def __init__(self):
  3.         pass
  4.  
  5.     def foo(self, arg1, arg2):
  6.         print arg1 + arg2
  7.  
  8.     def bar(self):
  9.         print "test"
  10.  
  11. class Child1(Parent):
  12.     def __init__(self):
  13.         Parent.__init__(self)
  14.  
  15.     def foo(self, arg1, arg2):
  16.         return arg1 * arg2
  17.  
  18.     def bar(self):
  19.         return "hello"
  20.  
  21. class Child2(Parent):
  22.     def __init__(self):
  23.         Parent.__init__(self)
  24.  
  25.     def foo(self, arg1, arg2):
  26.         return arg1 / arg2
  27.  
  28.     def bar(self):
  29.         return "world"
  30.  
  31. class Super(object):
  32.     def __init__(self, children):
  33.         self.children = children
  34.  
  35.     def __getattr__(self, name, *args):
  36.         if not self.__dict__.has_key(name):
  37.             self.__tempfunc = name
  38.             return self.runfuncs
  39.         else:
  40.             return self.__dict__[name]
  41.  
  42.     def runfuncs(self, *args):
  43.         for obj in self.children:
  44.             print "%s %s: " % (obj.__class__.__name__, self.__tempfunc),
  45.             ret = getattr(obj, self.__tempfunc)(*args)
  46.             if ret is not None:
  47.                 print ret
  48.  
  49. c1 = Child1()
  50. c2 = Child2()
  51. S = Super([c1, c2])
  52. S.foo(2, 4)
  53. S.bar()
  54.  
output:

Expand|Select|Wrap|Line Numbers
  1. Child1 foo:  8
  2. Child2 foo:  0
  3. Child1 bar:  hello
  4. Child2 bar:  world
  5.  
Mar 30 '09 #7

bvdet
Expert Mod 2.5K+
P: 2,851
Looks good, Xx r3negade. Thanks for sharing your solution.

-BV
Mar 30 '09 #8

Post your reply

Sign in to post your reply or Sign up for a free account.