[Tutor] Good approach regarding classes attributes

Peter Otten __peter__ at web.de
Sun Sep 7 16:06:01 CEST 2014


Juan Christian wrote:

> On Sun, Sep 7, 2014 at 5:04 AM, Peter Otten <__peter__ at web.de> wrote:
>>
>> It's not a good approach and it's not pythonic.
>>
>> In Python you should avoid accessor functions and (pseudo-)private
>> __attributes ("Python is not Java"). So
>>
>> class User:
>>     def __init__(self, id):
>>         self.id = id
>>         # load attributes
>>         self.personname = [personname from JSON]
>>         ...
>>
>> user = User(42)
>>
>> is certainly better. You might also consider making the class less
>> dependent
>> of the way you acquire the corresponding data:
>>
>> class User: # in Python 2: class User(object): ...
>>     def __init__(self, id, personname, ...)
>>         self.id = id
>>         self.personname = personname
>>         ...
>>     @classmethod
>>     def from_id(class_, id):
>>         # load attributes
>>         return User(id, personname, ...)
>>
>> jeff = User(42, "Jeff", ...)
>> jack = User.from_id(43)
> 
> 
> 
> Ok, no pseudo-private attributes. I read it in tutorials from 2013 and in
> the course that I'm taking that this would be a good pythonic way to deal
> with class attributes. They wouldn't be truly private, but someone using
> the program would see the " __ " in the beginning of the attribute and
> wouldn't call it directly, "because we are all adults and such", you
> saying that this approach doesn't exist in real life?

The double underscore plus name mangling is mainly to avoid name collisions 
in subclasses; to signal "this is private" a single underscore would 
suffice. But you then go on to make the attribute public via a a getter. In 
that case my first choice are normal attributes so that you can write

print(user.personname) # pythonic

instead of

print(user.get_personname()) # Javaism

If you want to prohibit the user from doing

user.personname = "Frankenstein" 

because the new name is not propagated to the database and the assignment 
puts your application into an inconsistent state which you want to avoid by 
some "bondage and discipline" you can change personname into a property:

class User:
    def __init__(self, id):
        ...
        self._personname = [as extracted from the JSON]

    @property
    def personname(self):
        return self._personname
                                

user = User(42)
print(user.personname) # implicitly calls the personname(self) method

> I can't give all the 8 attributes to '__init__' because I don't even have
> them. 

At least not now ;) My suggestion would decouple creation of the User 
instance and fetching of user-related data from a server.

> I would call it with ID only and them the API server would return me
> all the info, and then I would set them. I didn't learn '@classmethod'
> decoration yet, but I presume it would work as a 'get()', right? The thing
> is, where 'user with id 43' is stored? You get it using 'from_id' but we
> don't have any list in there the store users, I got confused in that part.

Maybe it becomes clearer with a small change. Instead of the classmethod you 
could use a normal function:

class User:
    def __init__(self, id, personname, ...):
        self.id = id
        self.personname = personname
        ...

def fetch_user_from_server(id):
    json_user = fetch data_from_server(id)
    return User(id, json_user["personname"], ...)

jim = fetch_user_from_server(42)

If you should later decide that you want to provide a way to allow entering 
new users you could use the User class for that, too:

def create_new_user():
    personname = input("Name: ") # in real code this would rather be a 
                                 # gui dialog or web page
    ...
    return User(None, personname, ...)

new_guy = create_new_user()
save_user_to_server(new_guy)

You don't have to worry that the __init__() method tries to load data for an 
inexistent user. 

But even if you are sure you'll never do that it is still a good idea to 
keep concerns separate, if only to write independent unit tests for the User 
class and the server access.




More information about the Tutor mailing list