[Tutor] Good approach regarding classes attributes
Steven D'Aprano
steve at pearwood.info
Sun Sep 7 20:01:01 CEST 2014
On Sun, Sep 07, 2014 at 12:00:15AM -0300, Juan Christian wrote:
> I'm writing a program that have a 'User' class. This class will have the
> following attributes:
>
> 1. id
> 2. personaname
> 3. lastlogoff
> 4. profileurl
> 5. avatar
> 6. realname
> 7. timecreated
> 8. loccountrycode
>
> I'm thinking about writing something like that: http://pastebin.com/7KHB2qQ8
For small code snippets, just include it in the body of your email. This
is a small code snippet :-)
class User():
def __init__(id):
self.__id = id
[URL Request to call API and get everything using the ID (JSON)]
self.__personaname = [JSON response personaname]
self.__lastlogoff = [JSON response personaname]
[...]
def get_id():
return __id
def get_personaname():
return __personaname
> Is it a good approach, is this phytonic?
Nope, it's more like Java than Python. And it's buggy.
Here are some questions you should ask yourself:
- Are you likely to subclass User? If you do subclass, is it reasonable
to treat the fields as part of the public API?
- Why are coupling the User class to the database? That makes it hard
to separate the construction of a User (say, for testing) from
database lookups.
This is my suggestion for a Pythonic approach, with some of the bugs
fixed, and using more Pythonic naming conventions.
class User(object):
# Class attribute is shared by all instances.
_database = XXX # reference to a database
def __init__(self, id, persona_name, last_logoff, profile_url,
avatar, real_name, time_created, loc_country_code):
# Data validation is left as an exercise.
self.id = id
self.persona_name = persona_name
self.last_logoff = last_logoff
# [etc. ...]
@classmethod
def fromid(cls, id):
args = cls._database.lookup_by_id(id) # or some method
return cls(*args)
And that's pretty much it for the initial version. Some features:
- There is a class attribute (and therefore shared by all instances)
called _database. In the Java world, I think that would be called
a "static variable". The leading underscore makes it a private
attribute by convention. By making this an attribute rather than
hard-coding it inside methods, it makes it easy to override during
testing:
saved_database = User._database
User._database = Mock()
# continue as usual, with no further changes needed
# when you are done:
User._database = saved_database
- The initialiser method __init__ takes eight explicit arguments, plus
"self". This enables you to create instances without reading them from
the database, e.g. creating them on the fly, reading from an INI file,
or any other source. This is especially useful during testing.
However, the downside of this is that you need to add argument
validation, since you can no longer assume the database has validated
all the values. Or, you can just trust the caller knows what they are
doing.
- There's an alternative constuctor offered, to support the case where
you do want to read the arguments from the database. So you can create
Users two ways:
instance = User(1234, 'fred', ...) # provide all the arguments
instance = User.fromid(1234) # or via database lookup
We can extend this minimal version. Suppose you want writing to the
attributes to update the database. We do this by making all the
attributes computed properties, with an extra private method.
class User(object):
# Class attribute is shared by all instances.
_database = XXX # reference to a database
# The init method stays the same.
def __init__(self, id, persona_name, last_logoff, profile_url,
avatar, real_name, time_created, loc_country_code):
# Data validation is left as an exercise.
self.id = id
self.persona_name = persona_name
self.last_logoff = last_logoff
# [etc. ...]
# But now we add a bunch of properties.
def _get_id(self):
[URL Request to call API and get everything using the ID (JSON)]
self.__personaname = [JSON response personaname]
self.__lastlogoff = [JSON response personaname]
[...]
def get_id():
return __id
def get_personaname():
return __personaname
> _______________________________________________
> Tutor maillist - Tutor at python.org
> To unsubscribe or change subscription options:
> https://mail.python.org/mailman/listinfo/tutor
More information about the Tutor
mailing list