Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.python > #97199 > unrolled thread

Question re class variable

Started byplewto@gmail.com
First post2015-09-29 02:27 -0700
Last post2015-09-29 13:11 +0200
Articles 11 — 8 participants

Back to article view | Back to comp.lang.python


Contents

  Question re class variable plewto@gmail.com - 2015-09-29 02:27 -0700
    Re: Question re class variable alister <alister.nospam.ware@ntlworld.com> - 2015-09-29 10:00 +0000
      Re: Question re class variable John Gordon <gordon@panix.com> - 2015-09-29 14:44 +0000
        Re: Question re class variable Dennis Lee Bieber <wlfraed@ix.netcom.com> - 2015-09-29 20:41 -0400
    Re: Question re class variable Antoon Pardon <antoon.pardon@rece.vub.ac.be> - 2015-09-29 12:40 +0200
      Re: Question re class variable Anssi Saari <as@sci.fi> - 2015-09-29 14:17 +0300
        Re: Question re class variable Antoon Pardon <antoon.pardon@rece.vub.ac.be> - 2015-09-29 14:02 +0200
        Re: Question re class variable Steven D'Aprano <steve@pearwood.info> - 2015-09-29 22:06 +1000
        Re: Question re class variable Dennis Lee Bieber <wlfraed@ix.netcom.com> - 2015-09-29 08:21 -0400
    Re: Question re class variable jmp <jeanmichel@sequans.com> - 2015-09-29 13:02 +0200
    Re: Question re class variable jmp <jeanmichel@sequans.com> - 2015-09-29 13:11 +0200

#97199 — Question re class variable

Fromplewto@gmail.com
Date2015-09-29 02:27 -0700
SubjectQuestion re class variable
Message-ID<3948d9cd-24b1-4a3b-8ed0-46bb60a8d738@googlegroups.com>
I have a perplexing problem with Python 3 class variables. I wish to generate an unique ID each time an instance of GameClass is created. There are two versions of the __gen_id method with test run results for each listed below the code. 

Originally I used the version which is now commented out. When a new instance was created it created an ID by appending the value of __instance_counter to the class name, it then checked the contents of of __instatance_registrty to see if this ID was already in use. If so it incremented the counter until it found an unused ID. This version works exactly as I expected.

Later I decided to get rid of __instance_registry and rely solely on the restricted access to __instance_counter and the fact that it is monotonically increasing to generate IDs. I wrote the second, simpler version of __gen_id to that end, but it doesn't work!  No doubt I'm overlooking something very simple here but I'm not seeing it. 

Any help appreciated. 


class GameObject:

    # __instance_registry = {"":None}
    __instance_counter = 0
    
    def __init__(self, name):
        self.__name = str(name)
        self.__id = self.__gen_id()

    # def __gen_id(self):
    #     ty = self.__class__.__name__
    #     id = ''
    #     while id in self.__instance_registry:
    #         id = '%s_%d' % (ty, self.__instance_counter)
    #         self.__instance_counter += 1
    #     self.__instance_registry[id] = self
    #     return id

    def __gen_id(self):
        ty = self.__class__.__name__
        id = '%s_%d' % (ty, self.__instance_counter)
        self.__instance_counter += 1
        return id

    def __str__(self):
        return "name = '%s'   id = '%s'" % (self.__name, self.__id)  
    

go1 = GameObject("GO1")
go2 = GameObject("GO2")
go3 = GameObject("GO3")
print(go1)
print(go2)
print(go3)


# Results with original __gen_id method
# name = 'GO1'   id = 'GameObject_0'
# name = 'GO2'   id = 'GameObject_1'
# name = 'GO3'   id = 'GameObject_2'


# Results with new simpler __gen_id method, __instance_counter not being incremented
# name = 'GO1'   id = 'GameObject_0'
# name = 'GO2'   id = 'GameObject_0'
# name = 'GO3'   id = 'GameObject_0'

[toc] | [next] | [standalone]


#97200

Fromalister <alister.nospam.ware@ntlworld.com>
Date2015-09-29 10:00 +0000
Message-ID<mudnfg$cqo$1@speranza.aioe.org>
In reply to#97199
On Tue, 29 Sep 2015 02:27:23 -0700, plewto wrote:

> I have a perplexing problem with Python 3 class variables. I wish to
> generate an unique ID each time an instance of GameClass is created.
> There are two versions of the __gen_id method with test run results for
> each listed below the code.
> 
> Originally I used the version which is now commented out. When a new
> instance was created it created an ID by appending the value of
> __instance_counter to the class name, it then checked the contents of of
> __instatance_registrty to see if this ID was already in use. If so it
> incremented the counter until it found an unused ID. This version works
> exactly as I expected.
> 
> Later I decided to get rid of __instance_registry and rely solely on the
> restricted access to __instance_counter and the fact that it is
> monotonically increasing to generate IDs. I wrote the second, simpler
> version of __gen_id to that end, but it doesn't work!  No doubt I'm
> overlooking something very simple here but I'm not seeing it.
> 
> Any help appreciated.
> 
> 
> class GameObject:
> 
>     # __instance_registry = {"":None}
>     __instance_counter = 0
>     
>     def __init__(self, name):
>         self.__name = str(name)
>         self.__id = self.__gen_id()
> 
>     # def __gen_id(self):
>     #     ty = self.__class__.__name__
>     #     id = ''
>     #     while id in self.__instance_registry:
>     #         id = '%s_%d' % (ty, self.__instance_counter)
>     #         self.__instance_counter += 1 #    
>     self.__instance_registry[id] = self #     return id
> 
>     def __gen_id(self):
>         ty = self.__class__.__name__
>         id = '%s_%d' % (ty, self.__instance_counter)
>         self.__instance_counter += 1 return id
> 
>     def __str__(self):
>         return "name = '%s'   id = '%s'" % (self.__name, self.__id)
>     
>     
> go1 = GameObject("GO1")
> go2 = GameObject("GO2")
> go3 = GameObject("GO3")
> print(go1)
> print(go2)
> print(go3)
> 
> 
> # Results with original __gen_id method # name = 'GO1'   id =
> 'GameObject_0'
> # name = 'GO2'   id = 'GameObject_1'
> # name = 'GO3'   id = 'GameObject_2'
> 
> 
> # Results with new simpler __gen_id method, __instance_counter not being
> incremented # name = 'GO1'   id = 'GameObject_0'
> # name = 'GO2'   id = 'GameObject_0'
> # name = 'GO3'   id = 'GameObject_0'

why reinvent the wheel?
why not simply use pythons builtin id function?
each new instance of an object is automatically assigned a unique ID


-- 
I'm a soldier, not a diplomat.  I can only tell the truth.
		-- Kirk, "Errand of Mercy", stardate 3198.9

[toc] | [prev] | [next] | [standalone]


#97211

FromJohn Gordon <gordon@panix.com>
Date2015-09-29 14:44 +0000
Message-ID<mue84o$l3l$1@reader1.panix.com>
In reply to#97200
In <mudnfg$cqo$1@speranza.aioe.org> alister <alister.nospam.ware@ntlworld.com> writes:

> why not simply use pythons builtin id function?
> each new instance of an object is automatically assigned a unique ID

It's only guaranteed to be unique for objects that exist at the same time.

If an object is created and destroyed and then another new object is
created, the ID of those two objects can be the same.

-- 
John Gordon                   A is for Amy, who fell down the stairs
gordon@panix.com              B is for Basil, assaulted by bears
                                -- Edward Gorey, "The Gashlycrumb Tinies"

[toc] | [prev] | [next] | [standalone]


#97221

FromDennis Lee Bieber <wlfraed@ix.netcom.com>
Date2015-09-29 20:41 -0400
Message-ID<mailman.250.1443573691.28679.python-list@python.org>
In reply to#97211
On Tue, 29 Sep 2015 14:44:40 +0000 (UTC), John Gordon <gordon@panix.com>
declaimed the following:

>In <mudnfg$cqo$1@speranza.aioe.org> alister <alister.nospam.ware@ntlworld.com> writes:
>
>> why not simply use pythons builtin id function?
>> each new instance of an object is automatically assigned a unique ID
>
>It's only guaranteed to be unique for objects that exist at the same time.
>
>If an object is created and destroyed and then another new object is
>created, the ID of those two objects can be the same.

	Since the code binds the object to the resultant entry
(registry[id]=self) -- it isn't going to go away without deliberately
rebinding the entry.

-- 
	Wulfraed                 Dennis Lee Bieber         AF6VN
    wlfraed@ix.netcom.com    HTTP://wlfraed.home.netcom.com/

[toc] | [prev] | [next] | [standalone]


#97201

FromAntoon Pardon <antoon.pardon@rece.vub.ac.be>
Date2015-09-29 12:40 +0200
Message-ID<mailman.232.1443523293.28679.python-list@python.org>
In reply to#97199
Op 29-09-15 om 11:27 schreef plewto@gmail.com:
> I have a perplexing problem with Python 3 class variables. I wish to generate an unique ID each time an instance of GameClass is created. There are two versions of the __gen_id method with test run results for each listed below the code.

The problem is that in python you can't change a class variable through an instance. The moment you
try, you create an instance attribute.

> class GameObject:
>
>     # __instance_registry = {"":None}
>     __instance_counter = 0
>     
>     def __init__(self, name):
>         self.__name = str(name)
>         self.__id = self.__gen_id()
>
>     def __gen_id(self):
>         ty = self.__class__.__name__
>         id = '%s_%d' % (ty, self.__instance_counter)
>         self.__instance_counter += 1

This last line doesn't work as expected. What happens is equivallent to
the following.

          self.__instance_counter = self.__instance_counter + 1

But the self.__instance_counter are two different things here. On the right hand
python finds that self has no __instance_counter attribute so it will fetch the
value from the class.

However on the left hand, python will create an attribute for self and assign the
value to it. Python will not rebind the class variable.

-- 
Antoon Pardon 

[toc] | [prev] | [next] | [standalone]


#97204

FromAnssi Saari <as@sci.fi>
Date2015-09-29 14:17 +0300
Message-ID<vg3k2r9wk34.fsf@coffee.modeemi.fi>
In reply to#97201
Antoon Pardon <antoon.pardon@rece.vub.ac.be> writes:

> Op 29-09-15 om 11:27 schreef plewto@gmail.com:
>> I have a perplexing problem with Python 3 class variables. I wish to
>> generate an unique ID each time an instance of GameClass is
>> created. There are two versions of the __gen_id method with test run
>> results for each listed below the code.
>
> The problem is that in python you can't change a class variable through an instance. The moment you
> try, you create an instance attribute.

That much is clear but why does his other version of __gen_id() work
(after a fashion)? It doesn't increment the class variable but the
instances get an incremental id.

The function was like this:

    def __gen_id(self):
        ty = self.__class__.__name__
        id = ''
        while id in self.__instance_registry:
            id = '%s_%d' % (ty, self.__instance_counter)
            self.__instance_counter += 1
        self.__instance_registry[id] = self
        return id

Also, is there any problem with incrementing
GameObject.__instance_counter from __gen_id()? I guess not?

[toc] | [prev] | [next] | [standalone]


#97206

FromAntoon Pardon <antoon.pardon@rece.vub.ac.be>
Date2015-09-29 14:02 +0200
Message-ID<mailman.236.1443528161.28679.python-list@python.org>
In reply to#97204
Op 29-09-15 om 13:17 schreef Anssi Saari:
> Antoon Pardon <antoon.pardon@rece.vub.ac.be> writes:
>
>> Op 29-09-15 om 11:27 schreef plewto@gmail.com:
>>> I have a perplexing problem with Python 3 class variables. I wish to
>>> generate an unique ID each time an instance of GameClass is
>>> created. There are two versions of the __gen_id method with test run
>>> results for each listed below the code.
>> The problem is that in python you can't change a class variable through an instance. The moment you
>> try, you create an instance attribute.
> That much is clear but why does his other version of __gen_id() work
> (after a fashion)? It doesn't increment the class variable but the
> instances get an incremental id.
>
> The function was like this:
>
>     def __gen_id(self):
>         ty = self.__class__.__name__
>         id = ''
>         while id in self.__instance_registry:
>             id = '%s_%d' % (ty, self.__instance_counter)
>             self.__instance_counter += 1
>         self.__instance_registry[id] = self
>         return id

Because you check against the class variable __instance_registry. That variable
isn't rebound, it is mutated, so it remains a class variable and can thus be used
to check which id's are already in use. So you increment your counter until the
corresponding id is not in the __instance_registry.

-- 
Antoon Pardon. 

[toc] | [prev] | [next] | [standalone]


#97207

FromSteven D'Aprano <steve@pearwood.info>
Date2015-09-29 22:06 +1000
Message-ID<560a7eb7$0$1607$c3e8da3$5496439d@news.astraweb.com>
In reply to#97204
On Tue, 29 Sep 2015 09:17 pm, Anssi Saari wrote:

[...]
>> The problem is that in python you can't change a class variable through
>> an instance. The moment you try, you create an instance attribute.
> 
> That much is clear but why does his other version of __gen_id() work
> (after a fashion)? It doesn't increment the class variable but the
> instances get an incremental id.
> 
> The function was like this:
> 
>     def __gen_id(self):
>         ty = self.__class__.__name__
>         id = ''
>         while id in self.__instance_registry:
>             id = '%s_%d' % (ty, self.__instance_counter)
>             self.__instance_counter += 1
>         self.__instance_registry[id] = self
>         return id


This works because it doesn't assign to self.__instance_registry itself, it
assigns to an item within the existing self.__instance_registry. So the
registry object (a dict?) gets modified in place, not re-bound or shadowed
by an instance attribute of the same name.



-- 
Steven

[toc] | [prev] | [next] | [standalone]


#97208

FromDennis Lee Bieber <wlfraed@ix.netcom.com>
Date2015-09-29 08:21 -0400
Message-ID<mailman.237.1443529284.28679.python-list@python.org>
In reply to#97204
On Tue, 29 Sep 2015 14:17:35 +0300, Anssi Saari <as@sci.fi> declaimed the
following:

>That much is clear but why does his other version of __gen_id() work
>(after a fashion)? It doesn't increment the class variable but the
>instances get an incremental id.
>
>The function was like this:
>
>    def __gen_id(self):
>        ty = self.__class__.__name__
>        id = ''
>        while id in self.__instance_registry:
>            id = '%s_%d' % (ty, self.__instance_counter)
>            self.__instance_counter += 1
>        self.__instance_registry[id] = self
>        return id
>

	They get an incremental ID because he constantly searches the
class-wide "instance registry" for the duplicate values -- which are always
being generated since the counter starts at 0 (+1) for each instance...
Would get quite slow if enough instances were to be created... While the
registry was initialized with

    # __instance_registry = {"":None}

Preloaded with a dummy value just so the first pass of the "while" loop
wouldn't drop out.

	Whereas the counter is a rebinding into a local, the registry is a
mutation of the classwide value, not a rebinding -- so it IS shared among
all.
-- 
	Wulfraed                 Dennis Lee Bieber         AF6VN
    wlfraed@ix.netcom.com    HTTP://wlfraed.home.netcom.com/

[toc] | [prev] | [next] | [standalone]


#97202

Fromjmp <jeanmichel@sequans.com>
Date2015-09-29 13:02 +0200
Message-ID<mailman.233.1443524549.28679.python-list@python.org>
In reply to#97199
On 09/29/2015 11:27 AM, plewto@gmail.com wrote:
> I have a perplexing problem with Python 3 class variables.

Your problem is that when assigning values to your class attribute, you 
are actually creating a instance attribute.


class Foo:
   bar = "I'm a class attribute"
   def __init__(self):
     self.bar = "I'm an instance attribute"

   def foo(self):
     print self.bar
     print Foo.bar
     # this is how you set a class attribute from an instance
     Foo.bar = "I am still a class attribute"
     print Foo.bar

Foo.foo()

I'm an instance attribute
I'm a class attribute
I am still a class attribute


What can be confusing is that assuming you never use the same name for a 
class an instance attribute (that would be bad code), you can access
your class attribute from the instance:

class Foo:
   bar = "I'm a class attribute"
   def foo(self):
     # python will look into the class scope if not found in the instance
     print self.bar # this is not an assignment so we're fine

Foo.foo()
I'm an class attribute

As side note and unrelated topic, your are using name mangling 
(attribute starting with __), are you sure you need it ? You need a 
strong motive to use this feature otherwise you're making things 
difficult for yourself without any benefit.

Finally here's how I'd code your id, to give some idea on alternative ways:

class GameObject:

   @property
   def id(self):
     return id(self) #use the builtin id function

print GameObject().id

Cheers,

JM

[toc] | [prev] | [next] | [standalone]


#97203

Fromjmp <jeanmichel@sequans.com>
Date2015-09-29 13:11 +0200
Message-ID<mailman.234.1443525084.28679.python-list@python.org>
In reply to#97199
On 09/29/2015 01:02 PM, jmp wrote:
> class GameObject:
>
>    @property
>    def id(self):
>      return id(self) #use the builtin id function
>
> print GameObject().id
>
> Cheers,
>
> JM

I should add that until you don't serialize your object you're fine.

If you need to serialize it, you may want to look at 
https://docs.python.org/3/library/uuid.html

import uuid

class GameObject:

   def __init__(self):
     self._id = None

   @property
   def id(self):
     if self._id is None:
       # make a UUID based on the host ID and current time
       self._id = uuid.uuid1()
     return self._id

[toc] | [prev] | [standalone]


Back to top | Article view | comp.lang.python


csiph-web