"Magic method" in python

Collapse
X
 
  • Time
  • Show
Clear All
new posts
  • Xx r3negade
    New Member
    • Apr 2008
    • 39

    "Magic method" in python

    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.
  • bvdet
    Recognized Expert Specialist
    • Oct 2006
    • 2851

    #2
    I was going to post an example! I'll post it anyway.
    Code:
    class Vector(Point):
    
        def __init__(self, x=0.0, y=0.0, z=0.0):
            Point.__init__(self, x, y, z)
    
        def __getattr__(self, name):
            if not self.__dict__.has_key(name):
                print "The attribute or method does not exist."
    Example:
    Code:
    >>> p = Vector(1,2,3)
    >>> p.z
    3.0
    >>> p.dist
    <bound method Vector.dist of Point(1.000000, 2.000000, 3.000000)>
    >>> p.dd
    The attribute or method does not exist.
    >>>
    Someone else may have the same question.

    -BV

    Comment

    • Xx r3negade
      New Member
      • Apr 2008
      • 39

      #3
      Is there any way to get the arguments that were passed, too?

      Something like this:
      Code:
      def __getattr__(name, *args):
          # whatever
      Except that obviously doesn't work...

      Comment

      • bvdet
        Recognized Expert Specialist
        • Oct 2006
        • 2851

        #4
        There is probably a better way.
        Code:
        class Vector(Point):
        
            def f(self, *args):
                return args
                    
            def __init__(self, x=0.0, y=0.0, z=0.0):
                Point.__init__(self, x, y, z)
        
            def __getattr__(self, name, *args):
                if not self.__dict__.has_key(name):
                    print "The attribute or method does not exist."
                    return self.f
                return self.__dict__[name]
        Code:
        >>> p1 = Vector(1,2,3)
        >>> p1.x
        1.0
        >>> p1.d
        The attribute or method does not exist.
        <bound method Vector.f of Point(1.000000, 2.000000, 3.000000)>
        >>> p1.d()
        The attribute or method does not exist.
        ()
        >>> p1.d('a', 'b')
        The attribute or method does not exist.
        ('a', 'b')
        >>>

        Comment

        • Xx r3negade
          New Member
          • Apr 2008
          • 39

          #5
          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 VirtualFileSyst em. 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 VirtualFileSyst em object and a DiskFileSystem object. Instead of doing this:
          Code:
          vfs1 = VirtualFileSystem()
          dfs1 = DiskFileSystem()
          vfs1.write("/some/path/to/file", "testing")
          dfs1.write("/some/path/to/file", "testing")
          I want to do this....

          Code:
          vfs1 = VirtualFileSystem()
          dfs1 = DiskFileSystem()
          sfs = SuperFileSystem([vfs1, dfs1])
          sfs.write("/some/path/to/file", "testing")
          As you may have guessed, the "SuperFileSyste m" 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?

          Comment

          • bvdet
            Recognized Expert Specialist
            • Oct 2006
            • 2851

            #6
            Almost anything is possible. A suggestion:
            Code:
            class FileSystem(object):
                def __init__(self):
                    pass
            
            class DiskFileSystem(FileSystem):
                def __init__(self):
                    super(DiskFileSystem, self).__init__()
            
                def write(self, fn):
                    print "Writing to DiskFileSystem, file %s" % fn
            
            class VirtualFileSystem(FileSystem):
                def __init__(self):
                    super(VirtualFileSystem, self).__init__()
            
                def write(self, fn):
                    print "Writing to VirtualFileSystem, file %s" % fn
            
            
            class SuperFileSystem(object):
                def __init__(self, *args):
                    self.args = args
                    pass
            
                def write(self, path, fn):
                    for arg in self.args:
                        arg.write('/'.join([path, fn]))
            
            vfs1 = VirtualFileSystem()
            dfs1 = DiskFileSystem()
            sfs = SuperFileSystem(vfs1, dfs1)
            sfs.write("/some/path/to/file", "testing")
            Output:
            >>> Writing to VirtualFileSyst em, file /some/path/to/file/testing
            Writing to DiskFileSystem, file /some/path/to/file/testing
            >>>

            Comment

            • Xx r3negade
              New Member
              • Apr 2008
              • 39

              #7
              If I followed your example, I would have to do this:
              Code:
                   def write(self, path, fn):
                       for arg in self.args:
                           arg.write('/'.join([path, fn]))
              over and over again for a bunch of different methods.

              Here is basically how I ended up doing this:

              Code:
              class Parent(object):
              	def __init__(self):
              		pass
              		
              	def foo(self, arg1, arg2):
              		print arg1 + arg2
              		
              	def bar(self):
              		print "test"
              		
              class Child1(Parent):
              	def __init__(self):
              		Parent.__init__(self)
              		
              	def foo(self, arg1, arg2):
              		return arg1 * arg2
              		
              	def bar(self):
              		return "hello"
              		
              class Child2(Parent):
              	def __init__(self):
              		Parent.__init__(self)
              		
              	def foo(self, arg1, arg2):
              		return arg1 / arg2
              		
              	def bar(self):
              		return "world"
              		
              class Super(object):
              	def __init__(self, children):
              		self.children = children
              	
              	def __getattr__(self, name, *args):
              		if not self.__dict__.has_key(name):
              			self.__tempfunc = name
              			return self.runfuncs
              		else:
              			return self.__dict__[name]
              			
              	def runfuncs(self, *args):
              		for obj in self.children:
              			print "%s %s: " % (obj.__class__.__name__, self.__tempfunc),
              			ret = getattr(obj, self.__tempfunc)(*args)
              			if ret is not None:
              				print ret
              			
              c1 = Child1()
              c2 = Child2()
              S = Super([c1, c2])
              S.foo(2, 4)
              S.bar()
              output:

              Code:
              Child1 foo:  8
              Child2 foo:  0
              Child1 bar:  hello
              Child2 bar:  world

              Comment

              • bvdet
                Recognized Expert Specialist
                • Oct 2006
                • 2851

                #8
                Looks good, Xx r3negade. Thanks for sharing your solution.

                -BV

                Comment

                Working...