creating many similar properties

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Lee Harr

    #1

    creating many similar properties


    I understand how to create a property like this:

    class RC(object):
    def _set_pwm(self, v):
    self._pwm01 = v % 256
    def _get_pwm(self):
    return self._pwm01
    pwm01 = property(_get_p wm, _set_pwm)


    But what if I have a whole bunch of these pwm properties?

    I made this:

    class RC(object):
    def _makeprop(name) :
    prop = '_%s' % name
    def _set(self, v):
    v_new = v % 256
    setattr(self, prop, v_new)
    def _get(self):
    return getattr(self, prop)
    return property(_get, _set)

    pwm01 = _makeprop('pwm0 1')
    pwm02 = _makeprop('pwm0 2')


    Not too bad, except for having to repeat the name.

    I would like to just have a list of pwm names and
    have them all set up like that. It would be nice if
    each one was set to a default value of 127 also....

    Any thoughts?

    Thanks for your time.
  • James Stroud

    #2
    Re: creating many similar properties

    Lee Harr wrote:
    I understand how to create a property like this:
    >
    class RC(object):
    def _set_pwm(self, v):
    self._pwm01 = v % 256
    def _get_pwm(self):
    return self._pwm01
    pwm01 = property(_get_p wm, _set_pwm)
    >
    >
    But what if I have a whole bunch of these pwm properties?
    >
    I made this:
    >
    class RC(object):
    def _makeprop(name) :
    prop = '_%s' % name
    def _set(self, v):
    v_new = v % 256
    setattr(self, prop, v_new)
    def _get(self):
    return getattr(self, prop)
    return property(_get, _set)
    >
    pwm01 = _makeprop('pwm0 1')
    pwm02 = _makeprop('pwm0 2')
    >
    >
    Not too bad, except for having to repeat the name.
    >
    I would like to just have a list of pwm names and
    have them all set up like that. It would be nice if
    each one was set to a default value of 127 also....
    >
    Any thoughts?
    >
    Thanks for your time.
    You want a "class factory". This can either be the esoteric "metaclass"
    (using "type"), which one can only understand for moments at a time, or
    something more grounded in intuition, or, a combo (for fun). You can
    probably tighten this a little, but this is the idea:

    def c_factory(cname , pname, num, modulus, default):
    def accessorize(pro p):
    def _get(self):
    return getattr(self, prop)
    def _set(self, v):
    v_new = v % modulus
    setattr(self, prop, v_new)
    return (_get, _set)
    def _init(self):
    for i in xrange(num):
    setattr(self, '%s%s' % (pname,i), default)
    _C = type(cname, (object,), {'__init__':_in it})
    for i in xrange(num):
    prop = '_%s%s' % (pname, i)
    setattr(_C, '%s%s' % (pname, i), property(*acces sorize(prop)))
    return _C

    E.g.:


    pydef c_factory(cname , pname, num, modulus, default):
    .... def accessorize(pro p):
    .... def _get(self):
    .... return getattr(self, prop)
    .... def _set(self, v):
    .... v_new = v % modulus
    .... setattr(self, prop, v_new)
    .... return (_get, _set)
    .... def _init(self):
    .... for i in xrange(num):
    .... setattr(self, '%s%s' % (pname,i), default)
    .... _C = type(cname, (object,), {'__init__':_in it})
    .... for i in xrange(num):
    .... prop = '_%s%s' % (pname, i)
    .... setattr(_C, '%s%s' % (pname, i), property(*acces sorize(prop)))
    .... return _C
    ....
    pyBob = c_factory('Bob' , 'bob', 4, 256, 128)
    pyb = Bob()
    pyb.bob0
    128
    pyb.bob1
    128
    pyb.bob1 = 258
    pyb.bob1
    2
    pyb.bob3
    128
    pydir(b)

    ['__class__',
    '__delattr__',
    '__dict__',
    '__doc__',
    '__getattribute __',
    '__hash__',
    '__init__',
    '__module__',
    '__new__',
    '__reduce__',
    '__reduce_ex__' ,
    '__repr__',
    '__setattr__',
    '__str__',
    '__weakref__',
    '_bob0',
    '_bob1',
    '_bob2',
    '_bob3',
    'bob0',
    'bob1',
    'bob2',
    'bob3']
    pyBob
    <class '__main__.Bob'>




    --
    James Stroud
    UCLA-DOE Institute for Genomics and Proteomics
    Box 951570
    Los Angeles, CA 90095


    Comment

    • Marc 'BlackJack' Rintsch

      #3
      Re: creating many similar properties

      In <n2fZg.3322$Ka1 .1194@news01.ro c.ny>, Lee Harr wrote:
      But what if I have a whole bunch of these pwm properties?
      >
      I made this:
      >
      class RC(object):
      def _makeprop(name) :
      prop = '_%s' % name
      def _set(self, v):
      v_new = v % 256
      setattr(self, prop, v_new)
      def _get(self):
      return getattr(self, prop)
      return property(_get, _set)
      >
      pwm01 = _makeprop('pwm0 1')
      pwm02 = _makeprop('pwm0 2')
      >
      >
      Not too bad, except for having to repeat the name.
      >
      I would like to just have a list of pwm names and
      have them all set up like that. It would be nice if
      each one was set to a default value of 127 also....
      >
      Any thoughts?
      Use `__getattr__()` and `__setattr__()` methods and a dictionary of names
      mapped to values and another that maps the names to default values for the
      modulo operation.

      Ciao,
      Marc 'BlackJack' Rintsch

      Comment

      • Michele Simionato

        #4
        Re: creating many similar properties

        Lee Harr wrote:
        I understand how to create a property like this:
        >
        class RC(object):
        def _set_pwm(self, v):
        self._pwm01 = v % 256
        def _get_pwm(self):
        return self._pwm01
        pwm01 = property(_get_p wm, _set_pwm)
        >
        >
        But what if I have a whole bunch of these pwm properties?
        >
        I made this:
        >
        class RC(object):
        def _makeprop(name) :
        prop = '_%s' % name
        def _set(self, v):
        v_new = v % 256
        setattr(self, prop, v_new)
        def _get(self):
        return getattr(self, prop)
        return property(_get, _set)
        >
        pwm01 = _makeprop('pwm0 1')
        pwm02 = _makeprop('pwm0 2')
        >
        >
        Not too bad, except for having to repeat the name.
        >
        I would like to just have a list of pwm names and
        have them all set up like that. It would be nice if
        each one was set to a default value of 127 also....
        >
        Any thoughts?
        >
        Yes, what about this?

        import sys

        def defprop(name, default=127):
        loc = sys._getframe(1 ).f_locals
        prop = '_%s' % name
        def _set(self, v):
        v_new = v % 256
        setattr(self, prop, v_new)
        def _get(self):
        return getattr(self, prop, default)
        loc[name] = property(_get, _set)

        class RC(object):
        defprop('pwm01' )
        defprop('pwm02' )

        rc = RC()

        print rc.pwm01 # 127
        print rc.pwm02 # 127
        rc.pwm02 = 1312
        print rc.pwm02 # 32

        This is a bit hackish, but I would prefer this over a metaclass
        solution. since it does not add
        any hidden magic to your class.

        Michele Simionato

        Comment

        • Carl Banks

          #5
          Re: creating many similar properties


          Lee Harr wrote:
          I understand how to create a property like this:
          >
          class RC(object):
          def _set_pwm(self, v):
          self._pwm01 = v % 256
          def _get_pwm(self):
          return self._pwm01
          pwm01 = property(_get_p wm, _set_pwm)
          >
          >
          But what if I have a whole bunch of these pwm properties?
          >
          I made this:
          >
          class RC(object):
          def _makeprop(name) :
          prop = '_%s' % name
          def _set(self, v):
          v_new = v % 256
          setattr(self, prop, v_new)
          def _get(self):
          return getattr(self, prop)
          return property(_get, _set)
          >
          pwm01 = _makeprop('pwm0 1')
          pwm02 = _makeprop('pwm0 2')
          >
          >
          Not too bad, except for having to repeat the name.
          >
          I would like to just have a list of pwm names and
          have them all set up like that. It would be nice if
          each one was set to a default value of 127 also....
          >
          Any thoughts?

          The metaclass solution. I use this idiom occasionally, whenever I want
          to fiddle with the class dict before letting the type constructor at
          it.

          class mod256metatype( type):
          def __new__(metatyp e,name,bases,cl sdict):
          for sym in clsdict.get('__ mod256__',()):
          prop = '_%s' % sym
          def _set(self,v):
          setattr(self,pr op,v%256)
          def _get(self):
          return getattr(self,pr op)
          clsdict[sym] = property(_get,_ set)
          return type.__new__(me tatype,name,bas es,clsdict)

          class RC(object):
          __metaclass__ = mod256metatype
          __mod256__ = ["pwm01","pw m02"]


          Carl

          Comment

          • George Sakkis

            #6
            Re: creating many similar properties

            Michele Simionato wrote:
            Lee Harr wrote:
            I understand how to create a property like this:

            class RC(object):
            def _set_pwm(self, v):
            self._pwm01 = v % 256
            def _get_pwm(self):
            return self._pwm01
            pwm01 = property(_get_p wm, _set_pwm)


            But what if I have a whole bunch of these pwm properties?

            I made this:

            class RC(object):
            def _makeprop(name) :
            prop = '_%s' % name
            def _set(self, v):
            v_new = v % 256
            setattr(self, prop, v_new)
            def _get(self):
            return getattr(self, prop)
            return property(_get, _set)

            pwm01 = _makeprop('pwm0 1')
            pwm02 = _makeprop('pwm0 2')


            Not too bad, except for having to repeat the name.

            I would like to just have a list of pwm names and
            have them all set up like that. It would be nice if
            each one was set to a default value of 127 also....

            Any thoughts?
            >
            Yes, what about this?
            >
            import sys
            >
            def defprop(name, default=127):
            loc = sys._getframe(1 ).f_locals
            prop = '_%s' % name
            def _set(self, v):
            v_new = v % 256
            setattr(self, prop, v_new)
            def _get(self):
            return getattr(self, prop, default)
            loc[name] = property(_get, _set)
            >
            class RC(object):
            defprop('pwm01' )
            defprop('pwm02' )
            >
            rc = RC()
            >
            print rc.pwm01 # 127
            print rc.pwm02 # 127
            rc.pwm02 = 1312
            print rc.pwm02 # 32
            >
            This is a bit hackish, but I would prefer this over a metaclass
            solution. since it does not add
            any hidden magic to your class.
            Why is this less hidden or magical than a metaclass ? I'd prefer the
            following instead:

            from itertools import chain, izip, repeat

            def ByteProperties( *names, **defaulted_nam es):
            def byte_property(n ame, default):
            return property(lambda self: getattr(self, name, default),
            lambda self,v: setattr(self, name, v%256))
            def make_class(clsn ame, bases, dict):
            for name,default in chain(izip(name s, repeat(127)),
            defaulted_names .iteritems()):
            assert name not in dict # sanity check
            dict[name] = byte_property(' _'+name, default)
            return type(clsname,ba ses,dict)
            return make_class


            class RC(object):
            __metaclass__ = ByteProperties( 'pwm01', pwm02=64)

            rc = RC()
            print rc.pwm01, rc.pwm02 # 127 64
            rc.pwm01 = 1312
            print rc.pwm01, rc.pwm02 # 32 64


            George

            Comment

            • George Sakkis

              #7
              Re: creating many similar properties

              Carl Banks wrote:
              Lee Harr wrote:
              I understand how to create a property like this:

              class RC(object):
              def _set_pwm(self, v):
              self._pwm01 = v % 256
              def _get_pwm(self):
              return self._pwm01
              pwm01 = property(_get_p wm, _set_pwm)


              But what if I have a whole bunch of these pwm properties?

              I made this:

              class RC(object):
              def _makeprop(name) :
              prop = '_%s' % name
              def _set(self, v):
              v_new = v % 256
              setattr(self, prop, v_new)
              def _get(self):
              return getattr(self, prop)
              return property(_get, _set)

              pwm01 = _makeprop('pwm0 1')
              pwm02 = _makeprop('pwm0 2')


              Not too bad, except for having to repeat the name.

              I would like to just have a list of pwm names and
              have them all set up like that. It would be nice if
              each one was set to a default value of 127 also....

              Any thoughts?
              >
              >
              The metaclass solution. I use this idiom occasionally, whenever I want
              to fiddle with the class dict before letting the type constructor at
              it.
              >
              class mod256metatype( type):
              def __new__(metatyp e,name,bases,cl sdict):
              for sym in clsdict.get('__ mod256__',()):
              prop = '_%s' % sym
              def _set(self,v):
              setattr(self,pr op,v%256)
              def _get(self):
              return getattr(self,pr op)
              clsdict[sym] = property(_get,_ set)
              return type.__new__(me tatype,name,bas es,clsdict)
              >
              class RC(object):
              __metaclass__ = mod256metatype
              __mod256__ = ["pwm01","pw m02"]
              There's a subtle common bug here: all _get and _set closures will refer
              to the last property only. You have to remember to write "def
              _set(self,v,pro p=prop)" and similarly for _get to do the right thing.
              By the way, I can't think of a case where the current behavior (i.e.
              binding the last value only) is the desired one. Is this just an
              implementation wart or am I missing something ?

              George

              Comment

              • Michele Simionato

                #8
                Re: creating many similar properties

                George Sakkis wrote:
                >
                Why is this less hidden or magical than a metaclass ?
                Because it does not use inheritance. It is not going to create
                properties on subclasses without
                you noticing it. Also, metaclasses are brittle: try to use them with
                __slots__, or with non-standard
                classes (i.e. extensions classes), or try to use multiple metaclasses.
                I wrote a paper
                about metaclasses abuses which should be published soon or later; you
                can see the draft
                here:


                Michele Simionato

                Comment

                • Michele Simionato

                  #9
                  Re: creating many similar properties

                  George Sakkis wrote:
                  >
                  from itertools import chain, izip, repeat
                  >
                  def ByteProperties( *names, **defaulted_nam es):
                  def byte_property(n ame, default):
                  return property(lambda self: getattr(self, name, default),
                  lambda self,v: setattr(self, name, v%256))
                  def make_class(clsn ame, bases, dict):
                  for name,default in chain(izip(name s, repeat(127)),
                  defaulted_names .iteritems()):
                  assert name not in dict # sanity check
                  dict[name] = byte_property(' _'+name, default)
                  return type(clsname,ba ses,dict)
                  return make_class
                  >
                  >
                  class RC(object):
                  __metaclass__ = ByteProperties( 'pwm01', pwm02=64)
                  Notice that you are NOT using a custom metaclass here, you are just
                  using the metaclass hook
                  and you will avoid all issues of custom metaclasses. This is exactly
                  the approach I advocate
                  in the paper I referred before, so I think your solution is pretty safe
                  in that respect. Still I
                  think in this particular problem avoiding the __metaclass__ at all is
                  possible and it should be
                  preferred, just for sake of simplicity, not of safety).

                  Michele Simionato

                  M

                  Comment

                  • Carl Banks

                    #10
                    Re: creating many similar properties


                    George Sakkis wrote:
                    Michele Simionato wrote:
                    import sys

                    def defprop(name, default=127):
                    loc = sys._getframe(1 ).f_locals
                    prop = '_%s' % name
                    def _set(self, v):
                    v_new = v % 256
                    setattr(self, prop, v_new)
                    def _get(self):
                    return getattr(self, prop, default)
                    loc[name] = property(_get, _set)

                    class RC(object):
                    defprop('pwm01' )
                    defprop('pwm02' )

                    rc = RC()

                    print rc.pwm01 # 127
                    print rc.pwm02 # 127
                    rc.pwm02 = 1312
                    print rc.pwm02 # 32

                    This is a bit hackish, but I would prefer this over a metaclass
                    solution. since it does not add
                    any hidden magic to your class.
                    >
                    Why is this less hidden or magical than a metaclass ?
                    Devil's Advocate: he did say "hidden magic TO YOUR CLASS".

                    If you use a (real) metaclass, then you have the icky feeling of a
                    class permanently tainted by the unclean metaclass (even though the
                    metaclass does nothing other than touch the class dict upon creation);
                    whereas if you use Michele Simionato's hack, the icky feeling of using
                    a stack frame object goes away after the property is created: you are
                    left with a clean untainted class.

                    Personally, the former doesn't make me feel icky at all.


                    Carl Banks

                    Comment

                    • Michele Simionato

                      #11
                      Re: creating many similar properties

                      Carl Banks wrote:
                      Devil's Advocate: he did say "hidden magic TO YOUR CLASS".
                      >
                      If you use a (real) metaclass, then you have the icky feeling of a
                      class permanently tainted by the unclean metaclass (even though the
                      metaclass does nothing other than touch the class dict upon creation);
                      whereas if you use Michele Simionato's hack, the icky feeling of using
                      a stack frame object goes away after the property is created: you are
                      left with a clean untainted class.
                      Yep, exactly.
                      Personally, the former doesn't make me feel icky at all.
                      Please, do this experiment: take all classes defined in the Python
                      standard library and add
                      to them a custom do-nothing metaclass. See what happens.


                      Michele Simionato

                      Comment

                      • Carl Banks

                        #12
                        Re: creating many similar properties

                        George Sakkis wrote:
                        There's a subtle common bug here: all _get and _set closures will refer
                        to the last property only. You have to remember to write "def
                        _set(self,v,pro p=prop)" and similarly for _get to do the right thing.
                        Sorry. My mistake.

                        By the way, I can't think of a case where the current behavior (i.e.
                        binding the last value only) is the desired one. Is this just an
                        implementation wart or am I missing something ?
                        def some_function(a ):
                        def printvars():
                        print "DEBUG: %r,%r,%r" % (a,b,i)
                        for i in some_range():
                        b = something(i)
                        printvars()

                        If you fix the value of the closure at function definition time,
                        printvars() above doesn't work. One way or another, someone's going to
                        get surprised. Better to let it be the experts.


                        Carl

                        Comment

                        • Carl Banks

                          #13
                          Re: creating many similar properties


                          Michele Simionato wrote:
                          Carl Banks wrote:
                          Devil's Advocate: he did say "hidden magic TO YOUR CLASS".

                          If you use a (real) metaclass, then you have the icky feeling of a
                          class permanently tainted by the unclean metaclass (even though the
                          metaclass does nothing other than touch the class dict upon creation);
                          whereas if you use Michele Simionato's hack, the icky feeling of using
                          a stack frame object goes away after the property is created: you are
                          left with a clean untainted class.
                          >
                          Yep, exactly.
                          >
                          Personally, the former doesn't make me feel icky at all.
                          >
                          Please, do this experiment: take all classes defined in the Python
                          standard library and add
                          to them a custom do-nothing metaclass. See what happens.
                          Do you expect the result to be better or worse than if you applied
                          stack frame hacks to the whole library?

                          Come on, I don't think anyone's under the impression we're being
                          indiscriminate here.


                          Carl Banks

                          (BTW, most of the standard library still uses old-style classes.)

                          Comment

                          • Michele Simionato

                            #14
                            Re: creating many similar properties

                            Carl Banks wrote:
                            Come on, I don't think anyone's under the impression we're being
                            indiscriminate here.
                            Ok, but I don't think that in the case at hand we should recommend a
                            metaclass
                            solution.

                            Michele Simionato

                            Comment

                            • Carl Banks

                              #15
                              Re: creating many similar properties

                              Michele Simionato wrote:
                              Carl Banks wrote:
                              Come on, I don't think anyone's under the impression we're being
                              indiscriminate here.
                              >
                              Ok, but I don't think that in the case at hand we should recommend a
                              metaclass
                              solution.
                              You sound as if you're avoiding metaclasses just for the sake of
                              avoiding them, which is just as bad as using them for the sake of using
                              them.

                              Here's how I see it: either it's ok to fiddle with the class dict, or
                              it isn't. If it's ok, then a metaclass is the way to do it. If it's
                              not ok to fiddle with the class dict, then he should be using
                              __setattr__, or creating properties longhand. Messing with frames is
                              not the answer for production code.

                              ....

                              Just for the hell of it, I decided to accept your challenge to run the
                              standard library with a different metaclass applied to all new-style
                              classes. I ran the 2.4.3 regression test, replacing the builtin object
                              with a class that used the mod256metaclass I presented in this thread.
                              The results:

                              229 tests OK.
                              34 tests failed:
                              test___all__ test_asynchat test_cgi test_cookielib test_copy
                              test_copy_reg test_cpickle test_decimal test_descr test_descrtut
                              test_email test_email_code cs test_httplib test_imaplib
                              test_inspect test_logging test_mailbox test_mimetools
                              test_mimetypes test_minidom test_pickle test_pyclbr
                              test_robotparse r test_sax test_sets test_socket test_socket_ssl
                              test_sundry test_timeout test_urllib test_urllib2 test_urllib2net
                              test_urllibnet test_xpickle

                              Not A-OK, but not exactly mass-pandemonium either. There were plenty
                              of modules used the the new base object and worked fine.

                              There were only two causes for failure:
                              1. A class attempting to use __weakref__ slot.
                              2. There were also a few metaclass conflicts.

                              IMO, neither of these failure modes argues against using a metaclass to
                              preprocess the class dict. The __weakref__ error is not applicable;
                              since it's an error to use it on any class with an instance dict. (In
                              fact, the metaclass wasn't even causing the error: the same error would
                              have occurred if I had replaced builtin object with an empty subclass
                              of itself, without the metaclass.)

                              The metaclass conflict would only occur in the present case only if
                              someone wanted to subclass it AND specify a different metaclass.
                              Arguing that metaclasses should be avoided just to guard against this
                              rare possibility is defensive to the extreme.

                              I did not uncover any kinds of subtle, unexpected behavior that can
                              occur when metaclasses do weird things. I know such things are
                              possible; how likely they are is another question. The tests I ran
                              didn't uncover any. So for now, the results of these tests don't seem
                              to support your point very well.

                              Carl


                              Appendix: Here's how I ran the test. I inserted the following code at
                              the top of Lib/test/regrtest.py, and
                              ran make test. I ran the tests on the Python 2.4.3 source tree, in
                              Linux.

                              =============== ========
                              import sys

                              class mod256metatype( type):
                              def __new__(metatyp e,name,bases,cl sdict):
                              print >sys.__stdout__ , \
                              "++++++++ Creating class %s of type mod256metatype" %
                              name
                              def makeprop(sym):
                              prop = '_%s' % sym
                              def _set(self,v):
                              setattr(self,pr op,v%256)
                              def _get(self):
                              return getattr(self,pr op)
                              return property(_get,_ set)
                              for sym in clsdict.get('__ mod256__',()):
                              clsdict[sym] = makeprop(sym)
                              return super(metatype
                              return type.__new__(me tatype,name,bas es,clsdict)

                              class _object:
                              __metaclass__ = mod256metatype

                              import __builtin__
                              __builtin__.obj ect = _object
                              =============== ========

                              Comment

                              Working...