Circular Class Logic

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • half.italian@gmail.com

    #1

    Circular Class Logic

    I have a set of classes that describe Files, Folders, etc., that I use
    often in my scripts for moving files around, getting a files
    extension, converting paths, changing permissions, etc It's very
    similar to Jason Orendorff's 'Path' library, and is very useful to
    me. The base class 'Data.py' stores all the basic attributes of any
    piece of data on disk, and it is then subclassed to represent files
    and folders with the attributes specific to that type of object.

    I recently made a new class 'Disk.py' that is a subclass of
    'Folder.py', to describe all of the attributes of any local or network
    disks attached to the computer. I subclassed it from Folder, because
    it really is a folder, it's just at the root of a drive. I'm
    realizing that these 'Disk' objects are pretty useful, and I came up
    with the idea to put a 'Disks' instance as a shared class variable
    (singleton?) within the base class ('Data') The idea being that any
    instance of a File, or Folder object would contain the list of Disk
    objects attached to the system, and could use them accordingly. Also
    important is that I would only need to gather the disk info once for
    any given running application and all the instances could use it
    (origianlly I was getting the disk info for each File/Folder object)

    After an hour of banging my head from getting "AttributeError :
    'module' object has no attribute 'Disks'" errors, I finally realized
    that I'm trying to include an instance in the base class that is a
    subclass of itself. Short of making 'Disk' no longer a subclass of
    Folder, is there any other way to include a subclassed instance in the
    base class of that object? (this is very hard to put in to words)

    ~Sean

  • Ben Finney

    #2
    Re: Circular Class Logic

    half.italian@gm ail.com writes:
    Short of making 'Disk' no longer a subclass of Folder, is there any
    other way to include a subclassed instance in the base class of that
    object? (this is very hard to put in to words)
    It's a little difficult to visualise what you're describing, but IIUC
    your problem is of this form:

    ===== foo.py =====
    import bar

    class Foo(bar.Bar):
    pass
    =====

    ===== bar.py =====
    import baz

    class Bar(baz.Baz):
    pass
    =====

    ===== baz.py =====
    import foo

    class Baz(foo.Foo):
    pass
    =====

    That is, each of the classes want to inherit from the others.

    The usual solution in these cases is to find the common required
    functionality and factor that out to a separate class that is then the
    superclass of two of the existing classes, breaking the circle.

    ===== wibble.py =====
    # no dependencies

    class Wibble(object):
    pass
    =====

    ===== foo.py =====
    import wibble
    import bar

    class Foo(wibble.Wibb le, bar.Bar):
    pass
    =====

    ===== baz.py =====
    import wibble

    class Baz(wibble.Wibb le):
    pass
    =====

    Note that Baz no longer subclasses foo.Foo, and both Foo and Baz get
    the common functionality they share from wibble.Wibble.

    --
    \ "Buy not what you want, but what you need; what you do not need |
    `\ is expensive at a penny." -- Cato, 234-149 BC, Relique |
    _o__) |
    Ben Finney

    Comment

    • half.italian@gmail.com

      #3
      Re: Circular Class Logic

      >
      That is, each of the classes want to inherit from the others.
      That's not exactly what I'm doing, but your comment still might help.
      I actually want to include an instance of a subclass in it's
      superclass like this:

      ===== foo.py =====
      import Baz

      class Foo:
      baz = Baz.Baz()

      def __init__(self):
      pass
      =====

      ===== bar.py =====
      import Foo

      class Bar(Foo.Foo):
      pass
      =====

      ===== baz.py =====
      import Bar

      class Baz(Bar.Bar):
      pass
      =====
      The usual solution in these cases is to find the common required
      functionality and factor that out to a separate class that is then the
      superclass of two of the existing classes, breaking the circle.
      >
      ===== wibble.py =====
      # no dependencies
      >
      class Wibble(object):
      pass
      =====
      >
      ===== foo.py =====
      import wibble
      import bar
      >
      class Foo(wibble.Wibb le, bar.Bar):
      pass
      =====
      >
      ===== baz.py =====
      import wibble
      >
      class Baz(wibble.Wibb le):
      pass
      =====
      >
      Note that Baz no longer subclasses foo.Foo, and both Foo and Baz get
      the common functionality they share from wibble.Wibble.
      I have to think about that for a bit and see if it makes sense to
      factor anything out. Thanks for the idea. Would that be the best
      solution considering the above description?

      ~Sean
      >
      --
      \ "Buy not what you want, but what you need; what you do not need |
      `\ is expensive at a penny." -- Cato, 234-149 BC, Relique |
      _o__) |
      Ben Finney

      Comment

      • Gabriel Genellina

        #4
        Re: Circular Class Logic

        <half.italian <atgmail.comwri tes:
        That's not exactly what I'm doing, but your comment still might help.
        I actually want to include an instance of a subclass in it's
        superclass like this:
        Move the initialization to another function.
        >
        ===== foo.py =====
        # import Baz
        Remove the line above
        >
        class Foo:
        # baz = Baz.Baz()
        Remove the line above
        >
        def __init__(self):
        pass
        =====
        and add this below:
        def initFoo():
        import baz
        Foo.baz = baz.Baz()
        initFoo()

        No changes in bar.py, baz.py
        Instead of initFoo, you could use a custom metaclass. Or a class decorator (if
        and when they become available...)
        The code above is an effective way of doing what you want. But I'd think about
        the actual need of doing such things - are you sure it's a good design?

        --
        Gabriel Genellina

        Comment

        • half.italian@gmail.com

          #5
          Re: Circular Class Logic

          Remove the line above
          and add this below:
          def initFoo():
          import baz
          Foo.baz = baz.Baz()
          initFoo()
          I got it to work, but I had to add a check to see if the class
          variable had been set..

          def initBaz():
          import Baz
          Foo.baz = Baz.Baz()

          class Foo:
          baz = None
          def __init__(self):
          if Foo.baz == None:
          Foo.baz = True
          initBaz()

          What exactly is being accomplished by having the init function outside
          of the class? If there is no check, wouldn't it just execute every
          time an object is instantiated anyway?
          Instead of initFoo, you could use a custom metaclass. Or a class decorator (if
          and when they become available...)
          I haven't tried either of those yet. The names scare me. :) Sounds
          like this might be a good time to explore them.
          The code above is an effective way of doing what you want. But I'd think about
          the actual need of doing such things - are you sure it's a good design?
          >
          I thought it was a good design, but now I'm not so sure. I'm
          untrained, so often I dont know the right way to do things. As I
          build this into the libraries, I'll keep an eye on how the code is
          developing and go from there. Thanks for the help!

          ~Sean

          Comment

          • Paul McGuire

            #6
            Re: Circular Class Logic

            On Mar 15, 7:01 am, half.ital...@gm ail.com wrote:
            Remove the line above
            and add this below:
            def initFoo():
            import baz
            Foo.baz = baz.Baz()
            initFoo()
            >
            I got it to work, but I had to add a check to see if the class
            variable had been set..
            >
            def initBaz():
            import Baz
            Foo.baz = Baz.Baz()
            >
            class Foo:
            baz = None
            def __init__(self):
            if Foo.baz == None:
            Foo.baz = True
            initBaz()
            >
            What exactly is being accomplished by having the init function outside
            of the class? If there is no check, wouldn't it just execute every
            time an object is instantiated anyway?
            >
            Instead of initFoo, you could use a custom metaclass. Or a class decorator (if
            and when they become available...)
            >
            I haven't tried either of those yet. The names scare me. :) Sounds
            like this might be a good time to explore them.
            >
            The code above is an effective way of doing what you want. But I'd think about
            the actual need of doing such things - are you sure it's a good design?
            >
            I thought it was a good design, but now I'm not so sure. I'm
            untrained, so often I dont know the right way to do things. As I
            build this into the libraries, I'll keep an eye on how the code is
            developing and go from there. Thanks for the help!
            >
            ~Sean
            Just initialize Folder at module level - see below.
            -- Paul

            class Folder(object):
            def __init__(self,p ath):
            self.path = path
            pass

            def __repr__(self):
            return self.__class__. __name__+"("+se lf.path+")"

            class Disk(Folder):
            def __init__(self,d riveLetter):
            super(Disk,self ).__init__(driv eLetter+":/")

            # hokey function to test if a drive letter points to a drive, I'm
            # sure there's a better way
            import os,string
            def isDisk(d):
            try:
            os.stat(d + ":/")
            return True
            except:
            return False

            # instead of calling some initFolder() method, just include this
            statement
            # at module level in the module that declares both classes
            Folder.allDisks = map(Disk,[d for d in string.uppercas e if isDisk(d)])

            # now can reference allDisks through Folder instances
            f = Folder("C:/temp")
            print f.allDisks


            prints:
            [Disk(C:/), Disk(D:/), Disk(E:/)]


            Comment

            • half.italian@gmail.com

              #7
              Re: Circular Class Logic

              Just initialize Folder at module level - see below.
              -- Paul
              >
              class Disk(Folder):
              def __init__(self,d riveLetter):
              super(Disk,self ).__init__(driv eLetter+":/")
              What is going on there? Is it just explicitly calling the super's
              init function? How is that really different from this:

              class Disk(Folder):
              def __init__(self,d riveLetter):
              Folder.Folder._ _init__(self.pa th) # ??? Being that Folder is the
              superclass?

              I'd like to be able to use the shared Disk objects in any class that
              is a subclass of Data. Will the above method still make the Disk
              objects available in File.py? Also, what if the classes are in
              different modules.

              I'll try to experiment with it this afternoon. I'm using more
              information than just the drive letter. What if the user wants to
              find out the unc path to the share given the drive letter, or copy a
              file to the local disk that is not a system disk that has the most
              available space on it, or wants to mount or unmount a network drive.

              pause...

              I'm trying to justify why the Folders/Files really need the drive info
              beyond just the paths and I'm having a hard time. I even just tried
              to give an example of how it would be used, and couldn't. It seems to
              make sense on a gut level, but maybe it's just as usefull on it's
              own. There's definitely justification for having Disks be a subclass
              of Folder, but not necessarily for there to be an instance of it
              available within the classes. All that kind of work will be external
              to the class.

              I think I'll backtrace and keep it separate. Thanks for letting me
              think it through.

              ~Sean

              Comment

              • Paul McGuire

                #8
                Re: Circular Class Logic

                On Mar 15, 1:55 pm, half.ital...@gm ail.com wrote:
                class Disk(Folder):
                def __init__(self,d riveLetter):
                super(Disk,self ).__init__(driv eLetter+":/")
                >
                What is going on there? Is it just explicitly calling the super's
                init function?
                What is going on is that Disk is being initialized with a single
                character, such as 'D', and then calling the superclass with 'D:/',
                the root folder on D. You're the one who said that Disk was a
                subclass of Folder (sorry, you called it "Data"), so I tried to come
                up with an example in which this made some kind of sense.
                How is that really different from this:
                >
                class Disk(Folder):
                def __init__(self,d riveLetter):
                Folder.Folder._ _init__(self.pa th) # ??? Being that Folder is the
                superclass?
                >
                Where did self.path come from? Even though Folder is the superclass,
                self.path doesn't exist until the Folder.__init__ method gets called.
                This ain't C++ you know. And as I said above, we are taking a single
                character drive letter, and calling the superclass init with the root
                directory of that drive, so these are .
                I'd like to be able to use the shared Disk objects in any class that
                is a subclass of Data. Will the above method still make the Disk
                objects available in File.py? Also, what if the classes are in
                different modules.
                >
                I'll try to experiment with it this afternoon. I'm using more
                information than just the drive letter. What if the user wants to
                find out the unc path to the share given the drive letter, or copy a
                file to the local disk that is not a system disk that has the most
                available space on it, or wants to mount or unmount a network drive.
                >
                Knock yourself out. I just hacked together these Folder/Disk classes
                by way of a half-baked-but-working example. My point is that to
                initialize a class level variable on class Foo, all that is needed is
                to assign it in the defining module, that is:

                class Foo:
                pass

                Foo.fooClassVar = "a class level variable"

                Now any Foo or sub-Foo can access fooClassVar. The type of
                fooClassVar can be anything you want, whether it is a Foo, subclass of
                Foo or whatever, as long as it has been defined by the time you assign
                fooClassVar.

                -- Paul

                Comment

                • Gabriel Genellina

                  #9
                  Re: Circular Class Logic

                  En Thu, 15 Mar 2007 09:01:07 -0300, <half.italian@g mail.comescribi รณ:
                  I got it to work, but I had to add a check to see if the class
                  variable had been set..
                  class Foo:
                  baz = None
                  def __init__(self):
                  if Foo.baz == None:
                  Foo.baz = True
                  initBaz()
                  This is a different approach. You are using instance initialization
                  (Foo.__init__) to finish class initialization. Foo.__init__ will be called
                  each time you create a Foo instance; that's why you had to check if
                  Foo.baz is None.
                  (BTW, Foo.baz=True is unneeded).
                  The only problem I can see is that the Foo class is not completely
                  initialized until its first instance is created; this may not be a problem
                  in your case.
                  What exactly is being accomplished by having the init function outside
                  of the class? If there is no check, wouldn't it just execute every
                  time an object is instantiated anyway?
                  What I intended was different:

                  ==========
                  C:\TEMP>type foo.py
                  class Foo:
                  baz = None

                  def __init__(self):
                  pass

                  def initFoo():
                  import baz
                  Foo.baz = baz.Baz()
                  initFoo()

                  C:\TEMP>type bar.py
                  import foo

                  class Bar(foo.Foo):
                  pass

                  C:\TEMP>type baz.py
                  import bar

                  class Baz(bar.Bar):
                  pass

                  C:\TEMP>type test.py
                  import foo,bar,baz

                  print foo.Foo.baz
                  f = foo.Foo()
                  print f.baz
                  b = bar.Bar()
                  print b.baz
                  print baz.Baz.baz

                  C:\TEMP>python test.py
                  <baz.Baz instance at 0x00AD7B20>
                  <baz.Baz instance at 0x00AD7B20>
                  <baz.Baz instance at 0x00AD7B20>
                  <baz.Baz instance at 0x00AD7B20>
                  ==========

                  initFoo is only executed when Foo is imported the first time. Note that
                  you can't do that if
                  I thought it was a good design, but now I'm not so sure. I'm
                  untrained, so often I dont know the right way to do things. As I
                  build this into the libraries, I'll keep an eye on how the code is
                  developing and go from there. Thanks for the help!
                  Usually Python code is quite concise and straightforward ; when I find
                  myself writing things too convoluted, I know it's time to
                  refactor/redesign. (Anyway some things remain not as simple as I'd like
                  :( )
                  Perhaps you later find the need for a different/better way.

                  --
                  Gabriel Genellina

                  Comment

                  • half.italian@gmail.com

                    #10
                    Re: Circular Class Logic

                    How is that really different from this:
                    >
                    class Disk(Folder):
                    def __init__(self,d riveLetter):
                    Folder.Folder._ _init__(self.pa th) # ??? Being that Folder is the
                    superclass?
                    >
                    Where did self.path come from? Even though Folder is the superclass,
                    self.path doesn't exist until the Folder.__init__ method gets called.
                    This ain't C++ you know.
                    My bad. I should have said:

                    class Disk(Folder):
                    def __init__(self,d riveLetter):
                    Folder.Folder._ _init__(self, driveLetter + ":/")
                    Knock yourself out. I just hacked together these Folder/Disk classes
                    by way of a half-baked-but-working example. My point is that to
                    initialize a class level variable on class Foo, all that is needed is
                    to assign it in the defining module, that is:
                    >
                    class Foo:
                    pass
                    >
                    Foo.fooClassVar = "a class level variable"
                    Now any Foo or sub-Foo can access fooClassVar. The type of
                    fooClassVar can be anything you want, whether it is a Foo, subclass of
                    Foo or whatever, as long as it has been defined by the time you assign
                    fooClassVar.
                    That was the first thing I tried, but because the libs were importing
                    each other, etc, it got hung up with wierd "This module doesn't exist"
                    errors, when it clearly did.

                    ~Sean



                    Comment

                    • Paul McGuire

                      #11
                      Re: Circular Class Logic

                      On Mar 15, 9:02 pm, half.ital...@gm ail.com wrote:
                      How is that (using 'super') really different from this:
                      >
                      class Disk(Folder):
                      def __init__(self,d riveLetter):
                      Folder.Folder._ _init__(self, driveLetter + ":/")
                      >
                      Google for "python super" - there is documentation on this, plus some
                      interesting c.l.py threads and blog scribblings on the behavior and
                      good/bad aspects of super. You may be better off using the explicit
                      class calls as you have. I always assumed that super, being added to
                      the language later, represented some form of improvement, but this may
                      not be 100% correct.

                      -- Paul

                      Comment

                      • greg

                        #12
                        Re: Circular Class Logic

                        Paul McGuire wrote:
                        I always assumed that super, being added to
                        the language later, represented some form of improvement, but this may
                        not be 100% correct.
                        It's not -- super is not a replacement for explicit
                        inherited method calls. It does something different,
                        and has different use cases.

                        It's usually not appropriate for __init__ calls,
                        because an __init__ method generally doesn't have
                        the same signature as that of its base class(es).
                        You're better off calling the base __init__
                        directly, then you know exactly which method
                        you're calling and what signature it has.

                        --
                        Greg

                        Comment

                        Working...