Reputation: 960
What I want to achieve:
1. Have a class variable keeping count of number of objects created
2. That variable should not be available to objects/others i.e. private to the class
3. If specific ID is not provided during init, use this counter variable to assign object.ID
I have following python code
class UserClass(object) :
__user_id_counter = 0
def __init__(self, UserID=__user_id_counter) :
self.UserID = UserID
__user_id_counter += 1
myuser = UserClass()
but I am getting
UnboundLocalError: local variable '_UserClass__user_id_counter' referenced before assignment
I am new to python so kindly help me here :)
Upvotes: 1
Views: 3080
Reputation: 90742
To start with, I'm renaming UserClass
to User
(we know it's a class) and UserID
to id
(it's in the User
class so no need to repeat the "User" bit, and Python style recommends lowercase), and __user_id_counter
to __id_counter
(ditto). Read PEP 8 for Python style guidelines.
So then, our starting point is this:
class User(object):
__id_counter = 0
def __init__(self, id=None) :
self.id = self.__id_counter if id is None else id
__id_counter += 1
myuser = User()
myuser2 = User()
myuser3 = User()
Now, the double leading underscore (without a double trailing underscore as in things like __init__
and __metaclass__
) is special, and is used for an implementation of class-private variables - read about private variables in the Python manual for more info. It triggers what's called "name mangling". What it ends up as is with __id_counter
being renamed to _User__id_counter
throughout the class. This is both useful and dangerous. In this case, I think it's probably just not necessary.
This can lead to a problem with subclasses.
class SpecialUser(User):
def __init__(self):
self.__id_counter
Now, SpecialUser()
will trigger an AttributeError: 'SpecialUser' object has no attribute '_SpecialUser__id_counter'
.
So then the question is, do you want subclasses to have the same ID counter, or their own ID counters?
If you want them to have the same ID counter, use User.__id_counter
. Don't use self.__class__.__id_counter
or type(self).__id_counter
with the += 1
, as this will, for a SpecialUser
instance, set SpecialUser._User__id_counter
to User._User__id_counter + 1
, and then SpecialUser
will use its own _User__id_counter
from this point onwards, which is far from what you want.
If you want them to have their own ID counters, start by using _id_counter
rather than __id_counter
, as the name mangling will take you in a direction you don't want to go.
There are a couple of approaches you can take:
Use a metaclass to give each class its own counter variable (a more advanced topic than I can be bothered writing about at the moment, ask for more details if you want to).
Put in a line _id_counter = 0
in each class definition. (If you don't, you'll run into trouble with ID starting point - make a lot of them, e.g. User(), User(), SpecialUser(), User(), SpecialUser(), SpecialUser(), User()
, and they'll have IDs of (respectively) 0, 1, 2, 2, 3, 4, 3
rather than 0, 1, 0, 2, 1, 2
.
An additional inefficiency which would arise from using the name mangling approach here would be that subclasses will have multiple counters in them - _User__id_counter
, _SpecialUser__id_counter
, etc.
Upvotes: 1
Reputation: 136208
To access __user_id_counter
an object or class reference is needed. In the argument list self
or UserClass
can not be accessed, hence:
class UserClass(object) :
__user_id_counter = 0
def __init__(self, UserID=None) :
self.UserID = self.__user_id_counter if UserID is None else UserID
UserClass.__user_id_counter += 1
Upvotes: 4
Reputation: 35039
You have to write self.__user_id_counter
. This also looks up class variables, if the instance variable is not found.
EDIT: Sudhi pointed out, that in the argument list, the self
declaration is not valid. To circumvent this problem try the following:
class UserClass(object) :
__user_id_counter = 0
def __init__(self, UserID=None) :
if UserID is None:
self.UserID = self.__user_id_counter
self.__class__.__user_id_counter += 1
else:
self.UserID = UserID
myuser = UserClass()
Upvotes: 1