Workaround for not having __set_name__ in Python versions before 3.6











up vote
3
down vote

favorite












In Python 3.6, I can use the __set_name__ hook to get the class attribute name of a descriptor. How can I achieve this in python 2.x?



This is the code which works fine in Python 3.6:



class IntField:
def __get__(self, instance, owner):
if instance is None:
return self
return instance.__dict__[self.name]

def __set__(self, instance, value):
if not isinstance(value, int):
raise ValueError('expecting integer')
instance.__dict__[self.name] = value

def __set_name__(self, owner, name):
self.name = name

class Example:
a = IntField()









share|improve this question
























  • I edited your question a little. __set_name__ was introduced in 3.6.
    – timgeb
    Nov 10 at 18:23















up vote
3
down vote

favorite












In Python 3.6, I can use the __set_name__ hook to get the class attribute name of a descriptor. How can I achieve this in python 2.x?



This is the code which works fine in Python 3.6:



class IntField:
def __get__(self, instance, owner):
if instance is None:
return self
return instance.__dict__[self.name]

def __set__(self, instance, value):
if not isinstance(value, int):
raise ValueError('expecting integer')
instance.__dict__[self.name] = value

def __set_name__(self, owner, name):
self.name = name

class Example:
a = IntField()









share|improve this question
























  • I edited your question a little. __set_name__ was introduced in 3.6.
    – timgeb
    Nov 10 at 18:23













up vote
3
down vote

favorite









up vote
3
down vote

favorite











In Python 3.6, I can use the __set_name__ hook to get the class attribute name of a descriptor. How can I achieve this in python 2.x?



This is the code which works fine in Python 3.6:



class IntField:
def __get__(self, instance, owner):
if instance is None:
return self
return instance.__dict__[self.name]

def __set__(self, instance, value):
if not isinstance(value, int):
raise ValueError('expecting integer')
instance.__dict__[self.name] = value

def __set_name__(self, owner, name):
self.name = name

class Example:
a = IntField()









share|improve this question















In Python 3.6, I can use the __set_name__ hook to get the class attribute name of a descriptor. How can I achieve this in python 2.x?



This is the code which works fine in Python 3.6:



class IntField:
def __get__(self, instance, owner):
if instance is None:
return self
return instance.__dict__[self.name]

def __set__(self, instance, value):
if not isinstance(value, int):
raise ValueError('expecting integer')
instance.__dict__[self.name] = value

def __set_name__(self, owner, name):
self.name = name

class Example:
a = IntField()






python python-2.7






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Nov 10 at 18:21









timgeb

43.6k106085




43.6k106085










asked Nov 10 at 17:35









Naga

566




566












  • I edited your question a little. __set_name__ was introduced in 3.6.
    – timgeb
    Nov 10 at 18:23


















  • I edited your question a little. __set_name__ was introduced in 3.6.
    – timgeb
    Nov 10 at 18:23
















I edited your question a little. __set_name__ was introduced in 3.6.
– timgeb
Nov 10 at 18:23




I edited your question a little. __set_name__ was introduced in 3.6.
– timgeb
Nov 10 at 18:23












2 Answers
2






active

oldest

votes

















up vote
1
down vote



accepted










You may be looking for metaclasses, with it you can process the class attributes at class creation time.



class FooDescriptor(object):
def __get__(self, obj, objtype):
print('calling getter')

class FooMeta(type):
def __init__(cls, name, bases, attrs):
for k, v in attrs.iteritems():
if issubclass(type(v), FooDescriptor):
print('FooMeta.__init__, attribute name is "{}"'.format(k))

class Foo(object):
__metaclass__ = FooMeta
foo = FooDescriptor()


f = Foo()
f.foo


Output:



FooMeta.__init__, attribute name is "foo"
calling getter


If you need to change the class before it is created you need to override __new__ instead of __init__ at your metaclass. See this answer for more information on this topic: Is there any reason to choose __new__ over __init__ when defining a metaclass?






share|improve this answer






























    up vote
    1
    down vote













    There are various solutions with different degrees of hackishness. I always liked to use a class decorator for this.



    class IntField(object):
    def __get__(self, instance, owner):
    if instance is None:
    return self
    return instance.__dict__[self.name]

    def __set__(self, instance, value):
    if not isinstance(value, int):
    raise ValueError('expecting integer')
    instance.__dict__[self.name] = value

    def with_intfields(*names):
    def with_concrete_intfields(cls):
    for name in names:
    field = IntField()
    field.name = name
    setattr(cls, name, field)
    return cls
    return with_concrete_intfields


    You can use it like this:



    @with_intfields('a', 'b')
    class Example(object):
    pass

    e = Example()


    Demo:



    $ python2.7 -i clsdec.py
    >>> [x for x in vars(Example) if not x.startswith('_')]
    ['a', 'b']
    >>> Example.a.name
    'a'
    >>> e.a = 3
    >>> e.b = 'test'
    [...]
    ValueError: expecting integer


    Make sure to explicitly subclass from object in Python 2.7, that got me tripped up when I drafted the first version of this answer.






    share|improve this answer























      Your Answer






      StackExchange.ifUsing("editor", function () {
      StackExchange.using("externalEditor", function () {
      StackExchange.using("snippets", function () {
      StackExchange.snippets.init();
      });
      });
      }, "code-snippets");

      StackExchange.ready(function() {
      var channelOptions = {
      tags: "".split(" "),
      id: "1"
      };
      initTagRenderer("".split(" "), "".split(" "), channelOptions);

      StackExchange.using("externalEditor", function() {
      // Have to fire editor after snippets, if snippets enabled
      if (StackExchange.settings.snippets.snippetsEnabled) {
      StackExchange.using("snippets", function() {
      createEditor();
      });
      }
      else {
      createEditor();
      }
      });

      function createEditor() {
      StackExchange.prepareEditor({
      heartbeatType: 'answer',
      convertImagesToLinks: true,
      noModals: true,
      showLowRepImageUploadWarning: true,
      reputationToPostImages: 10,
      bindNavPrevention: true,
      postfix: "",
      imageUploader: {
      brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
      contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
      allowUrls: true
      },
      onDemand: true,
      discardSelector: ".discard-answer"
      ,immediatelyShowMarkdownHelp:true
      });


      }
      });














       

      draft saved


      draft discarded


















      StackExchange.ready(
      function () {
      StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53241645%2fworkaround-for-not-having-set-name-in-python-versions-before-3-6%23new-answer', 'question_page');
      }
      );

      Post as a guest















      Required, but never shown

























      2 Answers
      2






      active

      oldest

      votes








      2 Answers
      2






      active

      oldest

      votes









      active

      oldest

      votes






      active

      oldest

      votes








      up vote
      1
      down vote



      accepted










      You may be looking for metaclasses, with it you can process the class attributes at class creation time.



      class FooDescriptor(object):
      def __get__(self, obj, objtype):
      print('calling getter')

      class FooMeta(type):
      def __init__(cls, name, bases, attrs):
      for k, v in attrs.iteritems():
      if issubclass(type(v), FooDescriptor):
      print('FooMeta.__init__, attribute name is "{}"'.format(k))

      class Foo(object):
      __metaclass__ = FooMeta
      foo = FooDescriptor()


      f = Foo()
      f.foo


      Output:



      FooMeta.__init__, attribute name is "foo"
      calling getter


      If you need to change the class before it is created you need to override __new__ instead of __init__ at your metaclass. See this answer for more information on this topic: Is there any reason to choose __new__ over __init__ when defining a metaclass?






      share|improve this answer



























        up vote
        1
        down vote



        accepted










        You may be looking for metaclasses, with it you can process the class attributes at class creation time.



        class FooDescriptor(object):
        def __get__(self, obj, objtype):
        print('calling getter')

        class FooMeta(type):
        def __init__(cls, name, bases, attrs):
        for k, v in attrs.iteritems():
        if issubclass(type(v), FooDescriptor):
        print('FooMeta.__init__, attribute name is "{}"'.format(k))

        class Foo(object):
        __metaclass__ = FooMeta
        foo = FooDescriptor()


        f = Foo()
        f.foo


        Output:



        FooMeta.__init__, attribute name is "foo"
        calling getter


        If you need to change the class before it is created you need to override __new__ instead of __init__ at your metaclass. See this answer for more information on this topic: Is there any reason to choose __new__ over __init__ when defining a metaclass?






        share|improve this answer

























          up vote
          1
          down vote



          accepted







          up vote
          1
          down vote



          accepted






          You may be looking for metaclasses, with it you can process the class attributes at class creation time.



          class FooDescriptor(object):
          def __get__(self, obj, objtype):
          print('calling getter')

          class FooMeta(type):
          def __init__(cls, name, bases, attrs):
          for k, v in attrs.iteritems():
          if issubclass(type(v), FooDescriptor):
          print('FooMeta.__init__, attribute name is "{}"'.format(k))

          class Foo(object):
          __metaclass__ = FooMeta
          foo = FooDescriptor()


          f = Foo()
          f.foo


          Output:



          FooMeta.__init__, attribute name is "foo"
          calling getter


          If you need to change the class before it is created you need to override __new__ instead of __init__ at your metaclass. See this answer for more information on this topic: Is there any reason to choose __new__ over __init__ when defining a metaclass?






          share|improve this answer














          You may be looking for metaclasses, with it you can process the class attributes at class creation time.



          class FooDescriptor(object):
          def __get__(self, obj, objtype):
          print('calling getter')

          class FooMeta(type):
          def __init__(cls, name, bases, attrs):
          for k, v in attrs.iteritems():
          if issubclass(type(v), FooDescriptor):
          print('FooMeta.__init__, attribute name is "{}"'.format(k))

          class Foo(object):
          __metaclass__ = FooMeta
          foo = FooDescriptor()


          f = Foo()
          f.foo


          Output:



          FooMeta.__init__, attribute name is "foo"
          calling getter


          If you need to change the class before it is created you need to override __new__ instead of __init__ at your metaclass. See this answer for more information on this topic: Is there any reason to choose __new__ over __init__ when defining a metaclass?







          share|improve this answer














          share|improve this answer



          share|improve this answer








          edited Nov 10 at 18:14

























          answered Nov 10 at 18:08









          geckos

          722615




          722615
























              up vote
              1
              down vote













              There are various solutions with different degrees of hackishness. I always liked to use a class decorator for this.



              class IntField(object):
              def __get__(self, instance, owner):
              if instance is None:
              return self
              return instance.__dict__[self.name]

              def __set__(self, instance, value):
              if not isinstance(value, int):
              raise ValueError('expecting integer')
              instance.__dict__[self.name] = value

              def with_intfields(*names):
              def with_concrete_intfields(cls):
              for name in names:
              field = IntField()
              field.name = name
              setattr(cls, name, field)
              return cls
              return with_concrete_intfields


              You can use it like this:



              @with_intfields('a', 'b')
              class Example(object):
              pass

              e = Example()


              Demo:



              $ python2.7 -i clsdec.py
              >>> [x for x in vars(Example) if not x.startswith('_')]
              ['a', 'b']
              >>> Example.a.name
              'a'
              >>> e.a = 3
              >>> e.b = 'test'
              [...]
              ValueError: expecting integer


              Make sure to explicitly subclass from object in Python 2.7, that got me tripped up when I drafted the first version of this answer.






              share|improve this answer



























                up vote
                1
                down vote













                There are various solutions with different degrees of hackishness. I always liked to use a class decorator for this.



                class IntField(object):
                def __get__(self, instance, owner):
                if instance is None:
                return self
                return instance.__dict__[self.name]

                def __set__(self, instance, value):
                if not isinstance(value, int):
                raise ValueError('expecting integer')
                instance.__dict__[self.name] = value

                def with_intfields(*names):
                def with_concrete_intfields(cls):
                for name in names:
                field = IntField()
                field.name = name
                setattr(cls, name, field)
                return cls
                return with_concrete_intfields


                You can use it like this:



                @with_intfields('a', 'b')
                class Example(object):
                pass

                e = Example()


                Demo:



                $ python2.7 -i clsdec.py
                >>> [x for x in vars(Example) if not x.startswith('_')]
                ['a', 'b']
                >>> Example.a.name
                'a'
                >>> e.a = 3
                >>> e.b = 'test'
                [...]
                ValueError: expecting integer


                Make sure to explicitly subclass from object in Python 2.7, that got me tripped up when I drafted the first version of this answer.






                share|improve this answer

























                  up vote
                  1
                  down vote










                  up vote
                  1
                  down vote









                  There are various solutions with different degrees of hackishness. I always liked to use a class decorator for this.



                  class IntField(object):
                  def __get__(self, instance, owner):
                  if instance is None:
                  return self
                  return instance.__dict__[self.name]

                  def __set__(self, instance, value):
                  if not isinstance(value, int):
                  raise ValueError('expecting integer')
                  instance.__dict__[self.name] = value

                  def with_intfields(*names):
                  def with_concrete_intfields(cls):
                  for name in names:
                  field = IntField()
                  field.name = name
                  setattr(cls, name, field)
                  return cls
                  return with_concrete_intfields


                  You can use it like this:



                  @with_intfields('a', 'b')
                  class Example(object):
                  pass

                  e = Example()


                  Demo:



                  $ python2.7 -i clsdec.py
                  >>> [x for x in vars(Example) if not x.startswith('_')]
                  ['a', 'b']
                  >>> Example.a.name
                  'a'
                  >>> e.a = 3
                  >>> e.b = 'test'
                  [...]
                  ValueError: expecting integer


                  Make sure to explicitly subclass from object in Python 2.7, that got me tripped up when I drafted the first version of this answer.






                  share|improve this answer














                  There are various solutions with different degrees of hackishness. I always liked to use a class decorator for this.



                  class IntField(object):
                  def __get__(self, instance, owner):
                  if instance is None:
                  return self
                  return instance.__dict__[self.name]

                  def __set__(self, instance, value):
                  if not isinstance(value, int):
                  raise ValueError('expecting integer')
                  instance.__dict__[self.name] = value

                  def with_intfields(*names):
                  def with_concrete_intfields(cls):
                  for name in names:
                  field = IntField()
                  field.name = name
                  setattr(cls, name, field)
                  return cls
                  return with_concrete_intfields


                  You can use it like this:



                  @with_intfields('a', 'b')
                  class Example(object):
                  pass

                  e = Example()


                  Demo:



                  $ python2.7 -i clsdec.py
                  >>> [x for x in vars(Example) if not x.startswith('_')]
                  ['a', 'b']
                  >>> Example.a.name
                  'a'
                  >>> e.a = 3
                  >>> e.b = 'test'
                  [...]
                  ValueError: expecting integer


                  Make sure to explicitly subclass from object in Python 2.7, that got me tripped up when I drafted the first version of this answer.







                  share|improve this answer














                  share|improve this answer



                  share|improve this answer








                  edited Nov 10 at 18:08

























                  answered Nov 10 at 17:55









                  timgeb

                  43.6k106085




                  43.6k106085






























                       

                      draft saved


                      draft discarded



















































                       


                      draft saved


                      draft discarded














                      StackExchange.ready(
                      function () {
                      StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53241645%2fworkaround-for-not-having-set-name-in-python-versions-before-3-6%23new-answer', 'question_page');
                      }
                      );

                      Post as a guest















                      Required, but never shown





















































                      Required, but never shown














                      Required, but never shown












                      Required, but never shown







                      Required, but never shown

































                      Required, but never shown














                      Required, but never shown












                      Required, but never shown







                      Required, but never shown







                      Popular posts from this blog

                      Bressuire

                      Vorschmack

                      Quarantine