mutable member, bug or ...

Sambo sambo at void.com
Mon Jun 12 04:03:46 EDT 2006


Bruno Desthuilliers wrote:
> Sambo a écrit :
> 
>> By accident I assigned int to a class member 'count' which was 
>> initialized to (empty) string and had no error till I tried to use it 
>> as string, obviously. Why was there no error on assignment( near the 
>> end ).
> 
> 
> Python is dynamically typed - which means that it's not the name that 
> holds type info, but the object itself. names are just, well, names...
> 
Hmm.. so I could have one instance with (int)count and (string)count?
(yay DBase flashback, BRRR)
I was going to initialize the vars in __init__() but somehow it didn't make sense to me( but left couple of them there).


> BTW, I failed to see where you assigned an int to the class attribute 
> 'count'. I just saw a try to call a string - which should raise a 
> TypeError.
> 
I must have fixed it before posting.
gr_info.count = gr_info.first = gr_info.last = 0

???   except ValueError:
       gr_info.count(0)  ???
not sure what I was thinking there (maybe I was trying to triple fault the CPU hehe)

>> class Cgroup_info:
> 
> 
> Do yourself (and anyone having to work on or with your code) a favour: 
> use new-style classes (ie : inherit from 'object'). And FWIW, the 
> convention for class names is CamelCase - preferably without MS-like 
> hungarian annotation.
> 
Those damn hungarians with their calculators and notations, only later did it occur to me to paste the "class ServerInfo():" statement above.

> this creates a local variable 'group_name', bound to an empty string. 
> Using the reference to the current instance (usually named 'self', and 
> always passed in as first param) is *not* optional.
> 
>>        count = "0"  #last time checked and processed/retrieved

In __init__() it was an oversight but after the class className()...
I may have thought it unnecessary, otherwise class consists only of a group of functions if as you say any vars should be created/initialized in __init__() hmmm.

> the string module is mostly deprecated. Use str object methods instead - 
>  or if you just want to create an int from it's representation as a 
> string, int(self.count).
> 

Are string object methods among others, things like:
words = sentence.split() 
This no longer works for me is it because I imported 'string' , 
didn't import something or didn't use "from string import *" instead.
( must be a year since I last played with python.)


> 
>>        gr_info.group_name = grp
> 
> 
> Tthis create a new instance attribute "group_name" on the gr_info 
> object. This instance attribute will shadow the class attribute of the 
> same name.
> 
Hmmm . so what is a class attribute good for?

> Also, FWIW, if you always know the value for group_name when 
> instanciating a Cgroup_info object, you might as well pass it to the 
> initializer.
> 
Good point.



>>        try:
>>            ind = self.group_list.index( grp )
> 
> 
> The common convention for indices in each and every language is 'i'. If 
> you really want a meaningful name, then group_index would be better.
> 
lol, well like in a book and increasingly on the net index in used to refer to a list. So... I guess subscribed_group_list_index(_pointer) might be workable. 

> Also, for this kind of lookups, dicts are really faster than lists.
> 
I am storing group count first last, although I am considering moving the numeric data elsewhere, for about 100 items .. I'll leave dictionaries for future learning.

>>            return ( gr_info )
> 
> 
> parenthesis here are useless (and FWIW, they would be just as useless in 
> C++).
> 
A habit, true in python , in C, I think I remember reading about return function and statement. I was important at some point in C or perhaps way back in Pascal.


>>        print ind
>>        if len( self.group_list[ind].split() ) == 4:
>>            gr_info.count = self.group_list[ind].split()[1]
>>            gr_info.first = self.group_list[ind].split()[2]        
>>            gr_info.last = self.group_list[ind].split()[3]
> 
> 
> group_list[ind] is the same as grp, isn't it ? if so, using grp directly 
> might be much more efficient *and* much more readable.
> 
no grp is the (string) group name that was used earlier to find the right 
item in the list.
> Also, you're calling 4 times the same method. This is highly 
> inefficient. Try this instead:
>        parts = grp.split()
>        if len(parts) == 4:
>          gr_info.count, gr_info.first, gr_info.last = parts[1:]
> 
> 
yes I realized that in another function but forgot about the unpacking assignment of a slice.

>> else:
>>            gr_info.count = gr_info.first = gr_info.last = "0"
> 
> 
> This style of "chained assignment" can be a real gotcha in Python. In 
> this case, it is safe since "0" is immutable. But using a mutable object 
> instead would lead to probably unexpected results. Try this:
> 
> a = b = []
> a.append(1)
> print b
I rarely do that even in C particularly when working with struct members, 
but now with shorter names  it is affordable. barely remembered reading about it.

> 
> You have to understand that Python "variables" (the correct name is 
> "bindings") are just the association (in a given namespace) of a name 
> and a *reference* (kind of a smart pointer) to an object.
> 
>>        return( gr_info )   
> 
> 
> Here's a possible rewrite of your code. It's certainly not how it should 
> be done, but (apart from going into wild guesses) it's difficult to come 
> up with anything better without knowing the specs. Anyway, it should 
> help you grasp a more pythonic way to do things:
> 
> class GroupInfo(object):
> 
>    def __init__(self, group_name, count="0", first="0", last=""):
>        self.group_name = group_name
>        self.count = count  #last time checked and processed/retrieved
>        self.first = first
>        self.last = last
> 
>        self.retrieval_type = "" # always , ask( if more than some 
> limit), none
>        self.date_checked = ""
>        self.time_checked = ""
>        self.new_count = ""
>        self.new_first = ""
>        self.new_last = ""
> 
>        # local storage maintanance vars
>        self.pointer_file = ""
>        self.message_file = ""
> 
>        #maintanance vars
>        self.cur_mess_num = 0
>        self.cur_mess_id = ""
> 
>    # if you want to store count as string
>    # but retrieve it as an int. The setter will
>    # be used by the initializer, and will then create
>    # the implementation attribute _count
>    def _get_count(self):
>        return int(self._count)
> 
>    def _set_count(self, value):
>        self._count = str(value)
> 
>    count = property(_get_count, _set_count)
> 
> 
> class ServerInfo(object):
>     def __init__(self, ???)
>         self._groups = {} # dict lookup is faster
>         # ...
>     def has_group(self, group_name):
>         return self._groups.has_key(group_name)
> 
>     def get_group_stat(self, group_name):
>         if not self.has_group(group_name):
>             return GroupInfo(group_name, "0")
> 
>         parts = group_name.split()
>         if len(parts) != 4:
>             parts = [None, "0", "0", "0"]
>         return GroupInfo(group_name, *(parts[1:4]))
> 
> 
> As a last point, I'd second Fredrik : don't try to write C++ in Python. 
> Learn to write Python instead. The (freely available) "Dive into Python" 
> book should be a good place to get started.
> 
> HTH

New class style eh, I haven't got a grasp of the old style and exceptions
only out of necessity. Either the Python exception docs were easier to understand or simply since I had to, to use those Inet classes , I finally broke down. I may have to go to C with this, for the GUI and  I don't think building list of 100,000 messages is realistic .  


Well Thank You for all the good pointers.



More information about the Python-list mailing list