Re: [Tutor] Good approach regarding classes attributes
> I've just again experienced a new employer that tells my students my > name is 'Van Den Broek' when I tell them that it is 'van den Broek.' > This is the third time this week I've encountered this as a > programming example. Perhaps the use of the example is responsible for > the false belief amongst programmers that a surname always starts with > a captial letter. (Also delightful is the view that no name can > contain spaces.) > My apologies! I'll try a different example next time using strip() to remove surrounding whitespace. ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] Good approach regarding classes attributes
On 7 September 2014 21:01, Danny Yoo wrote: > Let's use a concrete example: say that we'd like to make sure a > Person's name is always capitalized. We might try to enforce this > capitalization property in the constructor. Hi all, I've just again experienced a new employer that tells my students my name is 'Van Den Broek' when I tell them that it is 'van den Broek.' This is the third time this week I've encountered this as a programming example. Perhaps the use of the example is responsible for the false belief amongst programmers that a surname always starts with a captial letter. (Also delightful is the view that no name can contain spaces.) For the love of puppies, can people please stop using this example?! (Apologies to Danny; as the other two cases I saw recently were in books, it was easiest to lash out against this example.) notVanDenly yours, Brian vdB ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] Good approach regarding classes attributes
On 09/09/14 14:44, Peter Otten wrote: Is it not helpful to always put (object) as the parent, if the class is not itself a sub-class? The answer differs between Python 2 and 3. In Python 3 class C: # preferred in Python 3 pass Apologies, I should have mentioned that. I've been using Python3 almost exclusively since January this year and I'm starting to forget my Python 2 idioms... -- Alan G Author of the Learn to Program web site http://www.alan-g.me.uk/ http://www.flickr.com/photos/alangauldphotos ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] Good approach regarding classes attributes
On 09/09/2014 16:05, Joel Goldstick wrote: On Tue, Sep 9, 2014 at 10:02 AM, Sydney Shall wrote: On 09/09/2014 15:44, Peter Otten wrote: Sydney Shall wrote: On 08/09/2014 18:39, Alan Gauld wrote: On 08/09/14 15:17, Juan Christian wrote: One tiny tweak... class User(): You don't need the parens after User. You don;t have any superclasses so they do nothing. Python convention for an empty parent list is just to leave the parens off: class User: A simple question from a newbie, in response to this surprise. Is it not helpful to always put (object) as the parent, if the class is not itself a sub-class? The answer differs between Python 2 and 3. In Python 3 class C: # preferred in Python 3 pass and class C(object): pass are the same, so there is no point adding the explicit object inheritance. In Python 2 however class C: pass will create a "classic class" whereas class C(object): # preferred in Python 2 pass is a "newstyle class". The most notable difference between these is that properties work correctly only with newstyle classes. Therefore making all your classes "newstyle" is a good idea. And while I am writing, what does OP stand for in this list? Original Poster, as Leam says. ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor Thanks Peter, most helpful. I was taught with Python 2.7, so now I understand the advice. -- Sydney Shall ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor Please post in plain text My apologies. I thought I was. I will immediately change it. -- Sydney Shall ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] Good approach regarding classes attributes
On Tue, Sep 9, 2014 at 10:02 AM, Sydney Shall wrote: > On 09/09/2014 15:44, Peter Otten wrote: > > Sydney Shall wrote: > > On 08/09/2014 18:39, Alan Gauld wrote: > > On 08/09/14 15:17, Juan Christian wrote: > > One tiny tweak... > > class User(): > > You don't need the parens after User. You don;t have any superclasses > so they do nothing. Python convention for an empty parent list is just > to leave the parens off: > > class User: > > A simple question from a newbie, in response to this surprise. > Is it not helpful to always put (object) as the parent, if the class is > not itself a sub-class? > > The answer differs between Python 2 and 3. In Python 3 > > class C: # preferred in Python 3 > pass > > and > > class C(object): > pass > > are the same, so there is no point adding the explicit object inheritance. > > In Python 2 however > > class C: > pass > > will create a "classic class" whereas > > class C(object): # preferred in Python 2 > pass > > is a "newstyle class". The most notable difference between these is that > properties work correctly only with newstyle classes. Therefore making all > your classes "newstyle" is a good idea. > > And while I am writing, what does OP stand for in this list? > > Original Poster, as Leam says. > > > ___ > Tutor maillist - Tutor@python.org > To unsubscribe or change subscription options: > https://mail.python.org/mailman/listinfo/tutor > > Thanks Peter, most helpful. > I was taught with Python 2.7, so now I understand the advice. > > > -- > Sydney Shall > > ___ > Tutor maillist - Tutor@python.org > To unsubscribe or change subscription options: > https://mail.python.org/mailman/listinfo/tutor > Please post in plain text -- Joel Goldstick http://joelgoldstick.com ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] Good approach regarding classes attributes
On 09/09/2014 15:44, Peter Otten wrote: Sydney Shall wrote: On 08/09/2014 18:39, Alan Gauld wrote: On 08/09/14 15:17, Juan Christian wrote: One tiny tweak... class User(): You don't need the parens after User. You don;t have any superclasses so they do nothing. Python convention for an empty parent list is just to leave the parens off: class User: A simple question from a newbie, in response to this surprise. Is it not helpful to always put (object) as the parent, if the class is not itself a sub-class? The answer differs between Python 2 and 3. In Python 3 class C: # preferred in Python 3 pass and class C(object): pass are the same, so there is no point adding the explicit object inheritance. In Python 2 however class C: pass will create a "classic class" whereas class C(object): # preferred in Python 2 pass is a "newstyle class". The most notable difference between these is that properties work correctly only with newstyle classes. Therefore making all your classes "newstyle" is a good idea. And while I am writing, what does OP stand for in this list? Original Poster, as Leam says. ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor Thanks Peter, most helpful. I was taught with Python 2.7, so now I understand the advice. -- Sydney Shall ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] Good approach regarding classes attributes
On Mon, Sep 8, 2014 at 5:58 AM, Peter Otten <__pete...@web.de > wrote: > > PS: This is not about being pythonic, but it might be more convenient for > client code if you use datetime objects instead of timestamps: > > >>> import datetime > >>> last_logoff = datetime.datetime.utcfromtimestamp(1410065399) > >>> print(last_logoff) > 2014-09-07 04:49:59 > Yes, I'll do it for sure, the API response is indeed returned that way to make things easier. ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] Good approach regarding classes attributes
Sydney Shall wrote: > On 08/09/2014 18:39, Alan Gauld wrote: >> On 08/09/14 15:17, Juan Christian wrote: >> >> One tiny tweak... >> >>> class User(): >> >> You don't need the parens after User. You don;t have any superclasses >> so they do nothing. Python convention for an empty parent list is just >> to leave the parens off: >> >> class User: >> > A simple question from a newbie, in response to this surprise. > Is it not helpful to always put (object) as the parent, if the class is > not itself a sub-class? The answer differs between Python 2 and 3. In Python 3 class C: # preferred in Python 3 pass and class C(object): pass are the same, so there is no point adding the explicit object inheritance. In Python 2 however class C: pass will create a "classic class" whereas class C(object): # preferred in Python 2 pass is a "newstyle class". The most notable difference between these is that properties work correctly only with newstyle classes. Therefore making all your classes "newstyle" is a good idea. > And while I am writing, what does OP stand for in this list? Original Poster, as Leam says. ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] Good approach regarding classes attributes
On Tue, Sep 9, 2014 at 9:09 AM, Sydney Shall wrote: > And while I am writing, what does OP stand for in this list? "Original Poster". So I understand. Won't answer the Python question since I'm a newbie here myself. -- Mind on a Mission ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] Good approach regarding classes attributes
On 08/09/2014 18:39, Alan Gauld wrote: On 08/09/14 15:17, Juan Christian wrote: One tiny tweak... class User(): You don't need the parens after User. You don;t have any superclasses so they do nothing. Python convention for an empty parent list is just to leave the parens off: class User: A simple question from a newbie, in response to this surprise. Is it not helpful to always put (object) as the parent, if the class is not itself a sub-class? And while I am writing, what does OP stand for in this list? -- Sydney Shall ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] Good approach regarding classes attributes
Juan Christian wrote: >> >> > On Mon, Sep 8, 2014 at 5:58 AM, Peter Otten <__pete...@web.de> wrote: >> >> In that spirit here's an alternative implementation of the User class: >> >> from collections import namedtuple >> User = namedtuple( >> "User", >> "steamid personaname lastlogoff profileurl " >> "avatar timecreated countrycode") >> >> You may note that I refrained from making little improvements to the >> attribute names -- another odd preference of mine ;) -- which also helps >> simplify the fetch_users() implementation: >> >> ... >> for user in user_list: >> if user["communityvisibilitystate"] == 3: >> users.append(User._make(user[field] for field in User._fields)) >> ... > > > I didn't get the idea behind 'namedtuple' and your 'User._make(user[field] > for field in User._fields)', I'm still using the other way: > > import urllib.request > import json > > class User: > > def __init__(self, steamid, personaname, lastlogoff, profileurl, avatar, > timecreated, loccountrycode): > self.steam_id = steamid > self.persona_name = personaname > self.last_logoff = lastlogoff > self.profile_url = profileurl > self.avatar = avatar > self.time_created = timecreated > self.country_code = loccountrycode > > def fetch_users(*steamids): > users = [] > > req = urllib.request.urlopen(' > http://api.steampowered.com/ISteamUser/GetPlayerSummaries/v0002/?key=B9X20&steamids=' > + ','.join(steamids)) > user_list = json.loads(req.read().decode('utf-8'))["response"]["players"] > > for user in user_list: > if user["communityvisibilitystate"] == 3: > users.append(User(user["steamid"], user["personaname"], > user["lastlogoff"], user["profileurl"], user["avatar"], > user["timecreated"], user["loccountrycode"])) > > return users > > As you guys said, I'd better use the "all open" pythonic way and not try > to hide things, so here it's. Is this piece of code pythonic enough? Yes, I think this is simple enough that you can easily understand it when you look at it again in a year. There are no constructs that strike me as unidiomatic. It's time to stop pondering over the innards of this component and use it to build something useful. Good luck with that! PS: This is not about being pythonic, but it might be more convenient for client code if you use datetime objects instead of timestamps: >>> import datetime >>> last_logoff = datetime.datetime.utcfromtimestamp(1410065399) >>> print(last_logoff) 2014-09-07 04:49:59 ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] Good approach regarding classes attributes
> > > On Mon, Sep 8, 2014 at 5:58 AM, Peter Otten <__pete...@web.de> wrote: > > In that spirit here's an alternative implementation of the User class: > > from collections import namedtuple > User = namedtuple( > "User", > "steamid personaname lastlogoff profileurl " > "avatar timecreated countrycode") > > You may note that I refrained from making little improvements to the > attribute names -- another odd preference of mine ;) -- which also helps > simplify the fetch_users() implementation: > > ... > for user in user_list: > if user["communityvisibilitystate"] == 3: > users.append(User._make(user[field] for field in User._fields)) > ... I didn't get the idea behind 'namedtuple' and your 'User._make(user[field] for field in User._fields)', I'm still using the other way: import urllib.request import json class User: def __init__(self, steamid, personaname, lastlogoff, profileurl, avatar, timecreated, loccountrycode): self.steam_id = steamid self.persona_name = personaname self.last_logoff = lastlogoff self.profile_url = profileurl self.avatar = avatar self.time_created = timecreated self.country_code = loccountrycode def fetch_users(*steamids): users = [] req = urllib.request.urlopen(' http://api.steampowered.com/ISteamUser/GetPlayerSummaries/v0002/?key=B9X20&steamids=' + ','.join(steamids)) user_list = json.loads(req.read().decode('utf-8'))["response"]["players"] for user in user_list: if user["communityvisibilitystate"] == 3: users.append(User(user["steamid"], user["personaname"], user["lastlogoff"], user["profileurl"], user["avatar"], user["timecreated"], user["loccountrycode"])) return users As you guys said, I'd better use the "all open" pythonic way and not try to hide things, so here it's. Is this piece of code pythonic enough? ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] Good approach regarding classes attributes
On 08/09/14 15:17, Juan Christian wrote: Why normal attributes? Isn't it better to make these read-only as I won't ever need to modify them? And even if I modify them, it won't change in the Steam servers, only in my program, and I don't see any use for that, I need the 'real' values always, the 'real' thing only. Read only is not a good term for properties, they are not really read-only. You can still change the unmderlying attribute directly: >>> class C: ...def __init__(self,x): ... self._x = x ...@property ...def x(self): ... return self._x ... >>> c = C(66) >>> c.x 66 >>> c._x 66 >>> c.x = 22 Traceback (most recent call last): File "", line 1, in AttributeError: can't set attribute >>> c._x = 22 >>> c.x 22 Notice that although I can't set x, I can set _x! And it 'changes' x. The only thing that stops me is seeing the _x which tells me I shouldn't be messing with it. So in this scenario the property only buys you a tiny bit of protection, you are really still relying on the "We are all consenting adults" philosophy of Python... That's why we would normally only use normal attributes unless the property needed some munging inside the getter/setter method. If you really really don't trust your clients not to mess with _x then that's where you probably do need the __x version, combined with properties. which is where we came in! :-) (And its still not 100% foolproof) >>> class C: ...def __init__(self,x): ... self.__x = x ...@property ...def x(self): ... return self.__x ... >>> c = C(66) >>> c.x 66 >>> c.__x Traceback (most recent call last): File "", line 1, in AttributeError: 'C' object has no attribute '__x' >>> c.x = 22 Traceback (most recent call last): File "", line 1, in AttributeError: can't set attribute >>> c.__x = 22 >>> c.x 66 >>> c.__x 22 Notice I can still set/read __x from the client, but at least this time it does not change the x property value... >>> dir(c) ['_C__x', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '__x', 'x'] >>> Notice dir() shows that you now have two __x attributes, the original name-mangled version and the new local 'c' instance version. The only way to get total read-only working would be to write your own get attribute function - I think that is as near foolproof as you can get in Python. Python doesn't really make this kind of data hiding easy, its just not the Pythonic way. Python is not Java (or Smalltalk for that matter). -- Alan G Author of the Learn to Program web site http://www.alan-g.me.uk/ http://www.flickr.com/photos/alangauldphotos ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] Good approach regarding classes attributes
On 08/09/14 15:17, Juan Christian wrote: One tiny tweak... class User(): You don't need the parens after User. You don;t have any superclasses so they do nothing. Python convention for an empty parent list is just to leave the parens off: class User: -- Alan G Author of the Learn to Program web site http://www.alan-g.me.uk/ http://www.flickr.com/photos/alangauldphotos ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] Good approach regarding classes attributes
Juan Christian wrote: > On Mon, Sep 8, 2014 at 5:58 AM, Peter Otten <__pete...@web.de> wrote: >> Personally I'd use normal attributes, though. >> > > Why normal attributes? Isn't it better to make these read-only as I won't > ever need to modify them? And even if I modify them, it won't change in > the Steam servers, only in my program, and I don't see any use for that, I > need the 'real' values always, the 'real' thing only. Yes, but you know that those attributes should not be reassigned, and for everyone else a hint in the docstring will do. Basically I tend to write as little code as needed, or to put it another way: I'm a lazy bastard ;) In that spirit here's an alternative implementation of the User class: from collections import namedtuple User = namedtuple( "User", "steamid personaname lastlogoff profileurl " "avatar timecreated countrycode") You may note that I refrained from making little improvements to the attribute names -- another odd preference of mine ;) -- which also helps simplify the fetch_users() implementation: ... for user in user_list: if user["communityvisibilitystate"] == 3: users.append(User._make(user[field] for field in User._fields)) ... ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] Good approach regarding classes attributes
On Mon, Sep 8, 2014 at 5:58 AM, Peter Otten <__pete...@web.de> wrote: > > > Personally I'd use normal attributes, though. > Why normal attributes? Isn't it better to make these read-only as I won't ever need to modify them? And even if I modify them, it won't change in the Steam servers, only in my program, and I don't see any use for that, I need the 'real' values always, the 'real' thing only. On Mon, Sep 8, 2014 at 2:36 AM, Alan Gauld wrote: > > Also note the underscore. It improves readability to break multi-words > like that. > For example several others have quoted this as 'person name' rather than > 'persona name' - quite a different concept but hard to spot when all one > word... I tried to follow the variables from the Steam API, there they use 'personaname', 'lastlogoff', 'profileurl', 'loccountrycode' and so on, everything without ' _ ', but now I'm doing the way you said, thanks! File "D:\Documents\HomeBroker\user.py", line 11, in __init__ >> self._avatar = avatar >> AttributeError: can't set attribute >> > > Thats because you set the property name to the field name. > Notice the difference: > > @property > def profileurl(self): > return self._profileurl > > @property > def _avatar(self): > return self._avatar > > > The first sets the property to the non-underscore version > The second uses underscore for both property and field name, > thus making the field read-only. Be careful what you wish for... > > Another reason to only use properties when you *need* to > make them read only (or to do some processing on reads > or writes) Yes, my fault there, I didn't see the ' _ ' in the beginning of the function, but I do need them to be read-only, the user will never need to modify them, and if he tries to modify let's say the name, ' persona_name = "New Name Here" ', it would only change inside the program, the Steam Servers would still use the old name, the 'real' name. The new and now working code is the following: import urllib.request import json class User(): def __init__(self, steamid, personaname, lastlogoff, profileurl, avatar, timecreated, loccountrycode): self._steam_id = steamid self._persona_name = personaname self._last_logoff = lastlogoff self._profile_url = profileurl self._avatar = avatar self._time_created = timecreated self._country_code = loccountrycode @property def steam_id(self): return self._steam_id @property def persona_name(self): return self._persona_name @property def last_logoff(self): return self._last_logoff @property def profile_url(self): return self._profile_url @property def avatar(self): return self._avatar @property def time_created(self): return self._time_created @property def country_code(self): return self._country_code def fetch_users(*id_collection): steamid = [] users = [] for user_id in id_collection: steamid.append(user_id) req = urllib.request.urlopen(' http://api.steampowered.com/ISteamUser/GetPlayerSummaries/v0002/?key=B9FXD20&steamids=' + ','.join(steamid)) user_list = json.loads(req.read().decode('utf-8'))["response"]["players"] for user in user_list: if user["communityvisibilitystate"] == 3: # Only people with open profile and inventory are wanted users.append(User(user["steamid"], user["personaname"], user["lastlogoff"], user["profileurl"], user["avatar"], user["timecreated"], user["loccountrycode"])) return users Remember that I do need these attributes to be read-only, so I need to use the '@property' decoration. I modified the 'fetch_users' function in order to accept any number of IDs, this way I can fetch various profiles in one call. Any problems, suggestions now? ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] Good approach regarding classes attributes
Juan Christian wrote: > I'll definitely use the '@property' decoration. Thanks for the tip, Personally I'd use normal attributes, though. > so, a > different module to accommodate all the API requests and one for the > logic/code itself is a better approach, right? A separate function or method should be sufficient. > Creating new users won't exist, my program is like a Home Broker using the > Steam API. This 'User' class will be mainly used to properly store the > users data, I'll automatically and recursively go trough my (or any > person) friend-list and fetch users info and inventory and properly use it > in my favor. It is always a good idea to give such background right from the start. There are existing wrappers of that API. You might adopt one or at least have a look at the code to see how they are solving the issues you encounter. ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] Good approach regarding classes attributes
On 08/09/14 03:31, Juan Christian wrote: @property def steamid(self): return self._steamid Unless you specifically *need* these fields to be read-only you don't need the property declarations. Just use the _XXX convention to signal that they are *intended* to be private and allow clients to access them as myuser._persona_name Also note the underscore. It improves readability to break multi-words like that. For example several others have quoted this as 'person name' rather than 'persona name' - quite a different concept but hard to spot when all one word... File "D:\Documents\HomeBroker\user.py", line 11, in __init__ self._avatar = avatar AttributeError: can't set attribute Thats because you set the property name to the field name. Notice the difference: @property def profileurl(self): return self._profileurl @property def _avatar(self): return self._avatar The first sets the property to the non-underscore version The second uses underscore for both property and field name, thus making the field read-only. Be careful what you wish for... Another reason to only use properties when you *need* to make them read only (or to do some processing on reads or writes) HTH -- Alan G Author of the Learn to Program web site http://www.alan-g.me.uk/ http://www.flickr.com/photos/alangauldphotos ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] Good approach regarding classes attributes
@property def _avatar(self): return self._avatar >> >> Hi Joel, >> >> The above code looks strange to me. The method and the field name >> should not use the same name. > > ah! good catch Danny. I didn't write it, I was commenting on the OP code. > > But (and maybe this was discussed earlier in the thread), what value > is using the property decorator instead of just saving the data to > attributes? Let's first point to documentation that says what "property" is: https://docs.python.org/2/library/functions.html#property If you read it a bit, one of the key terms that should come up is "managed attribute". What do they mean by this? A managed attribute is an attribute that the class's definition actively controls. Let's use a concrete example: say that we'd like to make sure a Person's name is always capitalized. We might try to enforce this capitalization property in the constructor. ### class Person(object): def __init__(self, name): self.name = name.title() def greet(self): print("Hi, I'm %s" % self.name) p = Person("joel goldstick") p.greet() ### However, this does not stop clients from assigning directly to the name, # p.name = "joel goldstick" # and therefore breaking a desire to keep the name capitalized. So this might be a problem. So what we'd like is the following: to make sure that there's some kind of program logic that kicks in whenever we assign to Person.name. In some programming languages, we do this by marking the attribute name in some way that makes it clear not to access it directly, and we provide "setter" and "getter" methods, the code that can "manage" this attribute. # class Person(object): def __init__(self, name): self._name = name.title() def getName(self): return self._name def setName(self, name): self._name = name.title() def greet(self): print("Hi, I'm %s" % self._name) ## client code: p = Person("joel goldstick") print(p.getName()) p.greet() p.setName("juan christian") print(p.getName()) p.greet() # Python allows us to get "setter" and "getter"-like behavior while still allowing client code to use the attribute with what looks like direct attribute access: # class Person(object): def __init__(self, name): self._name = name.title() @property def name(self): return self._name @name.setter def name(self, newName): self._name = newName.title() def greet(self): print("Hi, I'm %s" % self._name) ## client code: p = Person("joel goldstick") print(p.name) p.greet() p.name= "juan christian" print(p.name) p.greet() # where now the client code looks simpler, but the class definition still gets to manage the attribute's value. Hopefully that helps to make the suggestions in this thread a little more understandable in context. Python's properties allow us to make the client code look direct but still allow for attribute management. ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] Good approach regarding classes attributes
On Sun, Sep 7, 2014 at 11:01 PM, Danny Yoo wrote: >>> @property >>> def _avatar(self): >>> return self._avatar > > Hi Joel, > > The above code looks strange to me. The method and the field name > should not use the same name. ah! good catch Danny. I didn't write it, I was commenting on the OP code. But (and maybe this was discussed earlier in the thread), what value is using the property decorator instead of just saving the data to attributes? -- Joel Goldstick http://joelgoldstick.com ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] Good approach regarding classes attributes
>> @property >> def _avatar(self): >> return self._avatar Hi Joel, The above code looks strange to me. The method and the field name should not use the same name. ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] Good approach regarding classes attributes
On Sun, Sep 7, 2014 at 10:31 PM, Juan Christian wrote: > So... I tried to follow all what you guys said: > > user.py module: > > import urllib.request > import json > > class User(): > > def __init__(self, steamid, personaname, lastlogoff, profileurl, avatar, > timecreated, loccountrycode): > self._steamid = steamid > self._personaname = personaname > self._lastlogoff = lastlogoff > self._profileurl = profileurl > self._avatar = avatar > self._timecreated = timecreated > self._loccountrycode = loccountrycode > > > @property > def steamid(self): >return self._steamid I don't understand the purpose of the decorator and the _business why not self.timecreated = timecreated etc? > > @property > def personaname(self): > return self._personaname > Unless the formatting got screwed up in the email, all or these return statements need to be indented > @property > def lastlogoff(self): > return self._lastlogoff > > @property > def profileurl(self): > return self._profileurl > > @property > def _avatar(self): > return self._avatar > > @property > def _timecreated(self): > return self._timecreated > > @property > def _loccountrycode(self): > return self._loccountrycode > > > def fetch_user(steamid): > req = > urllib.request.urlopen('http://api.steampowered.com/ISteamUser/GetPlayerSummaries/v0002/?key=B9F55D955257F1EDC9B6D217B94FCD20&steamids=' > + steamid) > content = json.loads(req.read().decode('utf-8'))["response"]["players"][0] > print("DEBUG ONLY: " + content["avatar"] + "\n") try this: print content > > return User(content["steamid"], content["personaname"], > content["lastlogoff"], content["profileurl"], content["avatar"], > content["timecreated"], content["loccountrycode"]) > > > main module: > > from user import User > > u = User.fetch_user("76561198067618735") > print(u) > > > console output: > > DEBUG ONLY: > http://media.steampowered.com/steamcommunity/public/images/avatars/da/da259bfaef7fe7c2521de78433977a6c006217 > c5.jpg > > Traceback (most recent call last): > File ".\main.py", line 3, in > u = User.fetch_user("76561198067618735") > File "D:\Documents\HomeBroker\user.py", line 50, in fetch_user > return User(content["steamid"], content["personaname"], > content["lastlogoff"], content["profileurl"], content["avata > r"], content["timecreated"], content["loccountrycode"]) > File "D:\Documents\HomeBroker\user.py", line 11, in __init__ > self._avatar = avatar > AttributeError: can't set attribute > > > Why am I getting this "AttributeError: can't set attribute" specifically > when trying to set 'self._avatar = avatar'? Why not print out all of the content data. Something around avatar might look strange. > > Does my code in user.py module follows the pythonic way now? > > ___ > Tutor maillist - Tutor@python.org > To unsubscribe or change subscription options: > https://mail.python.org/mailman/listinfo/tutor > -- Joel Goldstick http://joelgoldstick.com ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] Good approach regarding classes attributes
So... I tried to follow all what you guys said: user.py module: import urllib.request import json class User(): def __init__(self, steamid, personaname, lastlogoff, profileurl, avatar, timecreated, loccountrycode): self._steamid = steamid self._personaname = personaname self._lastlogoff = lastlogoff self._profileurl = profileurl self._avatar = avatar self._timecreated = timecreated self._loccountrycode = loccountrycode @property def steamid(self): return self._steamid @property def personaname(self): return self._personaname @property def lastlogoff(self): return self._lastlogoff @property def profileurl(self): return self._profileurl @property def _avatar(self): return self._avatar @property def _timecreated(self): return self._timecreated @property def _loccountrycode(self): return self._loccountrycode def fetch_user(steamid): req = urllib.request.urlopen(' http://api.steampowered.com/ISteamUser/GetPlayerSummaries/v0002/?key=B9F55D955257F1EDC9B6D217B94FCD20&steamids=' + steamid) content = json.loads(req.read().decode('utf-8'))["response"]["players"][0] print("DEBUG ONLY: " + content["avatar"] + "\n") return User(content["steamid"], content["personaname"], content["lastlogoff"], content["profileurl"], content["avatar"], content["timecreated"], content["loccountrycode"]) main module: from user import User u = User.fetch_user("76561198067618735") print(u) console output: DEBUG ONLY: http://media.steampowered.com/steamcommunity/public/images/avatars/da/da259bfaef7fe7c2521de78433977a6c006217 c5.jpg Traceback (most recent call last): File ".\main.py", line 3, in u = User.fetch_user("76561198067618735") File "D:\Documents\HomeBroker\user.py", line 50, in fetch_user return User(content["steamid"], content["personaname"], content["lastlogoff"], content["profileurl"], content["avata r"], content["timecreated"], content["loccountrycode"]) File "D:\Documents\HomeBroker\user.py", line 11, in __init__ self._avatar = avatar AttributeError: can't set attribute Why am I getting this "AttributeError: can't set attribute" specifically when trying to set 'self._avatar = avatar'? Does my code in user.py module follows the pythonic way now? ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] Good approach regarding classes attributes
On Mon, Sep 08, 2014 at 04:01:01AM +1000, Steven D'Aprano wrote a bunch of stuff about the User class... Ah, sorry guys, I did *not* intend to send that post. It's probably a bit incoherent, and certainly unfinished. I hit the wrong key and my mail program sent it. If I get time to finish it tomorrow, I'll resend it. (But, alas, I probably won't. Sorry.) -- Steven ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] Good approach regarding classes attributes
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@python.org > To unsubscribe or change subscription options: > https://mail.python.org/mailman/listinfo/tutor ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] Good approach regarding classes attributes
> > > On Sun, Sep 7, 2014 at 5:04 AM, Peter Otten <__pete...@web.de> wrote: > > > 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) > I'll definitely use the '@property' decoration. Thanks for the tip, so, a different module to accommodate all the API requests and one for the logic/code itself is a better approach, right? > 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. Creating new users won't exist, my program is like a Home Broker using the Steam API. This 'User' class will be mainly used to properly store the users data, I'll automatically and recursively go trough my (or any person) friend-list and fetch users info and inventory and properly use it in my favor. Here is an example of JSON response from the Steam API: { "response": { "players": [ { "steamid": "76561198067618735", "communityvisibilitystate": 3, "profilestate": 1, "personaname": "wildee14", "lastlogoff": 1410065399, "commentpermission": 1, "profileurl": "http://steamcommunity.com/id/wildee14/";, "avatar": "http://media.steampowered.com/steamcommunity/public/images/avatars/da/da259bfaef7fe7c2521de78433977a6c006217c5.jpg";, "avatarmedium": "http://media.steampowered.com/steamcommunity/public/images/avatars/da/da259bfaef7fe7c2521de78433977a6c006217c5_medium.jpg";, "avatarfull": "http://media.steampowered.com/steamcommunity/public/images/avatars/da/da259bfaef7fe7c2521de78433977a6c006217c5_full.jpg";, "personastate": 0, "realname": "wildee", "primaryclanid": "103582791429571843", "timecreated": 1342807007, "personastateflags": 0, "loccountrycode": "US" } ] } } ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] Good approach regarding classes attributes
Juan Christian wrote: > On Sun, Sep 7, 2014 at 5:04 AM, Peter Otten <__pete...@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. ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] Good approach regarding classes attributes
On Sun, Sep 7, 2014 at 5:04 AM, Peter Otten <__pete...@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? I can't give all the 8 attributes to '__init__' because I don't even have them. 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. ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] Good approach regarding classes attributes
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: > 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] > [...] > > def get_id(): > return __id > > def get_personaname(): > return __personaname > > [...] > Is it a good approach, is this phytonic? 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) ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] Good approach regarding classes attributes
On 07/09/14 04:00, 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 When its a short bit of code (<100 lines) just put it in the email body... 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] [...] This is fine although the question of whether you need the attributes to be private needs to be considered carefully on a per attribute basis. def get_id(): return __id def get_personaname(): return __personaname But this style is not Pythonic. If you have a method that just returns the attribute its better to just make the attribute non-private and allow users to access it directly. In fact even if you wanted to do some processing around the access, rather than have lots of getXXX methods it would be more Pythonic to write get/set methods but then declare the attribute as a property and hide the get/set methods so that, to the user, it looks like direct access. get/set methods are a very Java-ish kind of style but not really Pythonic. For most cases direct access is preferred. Also, as a matter of style/convention, the class name is usually CamelCased so it would be class User: Similarly attribute names are usually spaced using underscores so personaname becomes persona_name etc. -- Alan G Author of the Learn to Program web site http://www.alan-g.me.uk/ http://www.flickr.com/photos/alangauldphotos ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] Good approach regarding classes attributes
Ops, sorry. Pastebin @ line 9: It's [JSON response lastlogoff] On Sun, Sep 7, 2014 at 12:00 AM, 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 > > Is it a good approach, is this phytonic? > ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor
[Tutor] Good approach regarding classes attributes
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 Is it a good approach, is this phytonic? ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor