[Tutor] Tutor Digest, Vol 104, Issue 60

Osemeka Osuagwu abasiemeka at gmail.com
Tue Oct 16 09:53:34 CEST 2012


On Sun, 14 Oct 2012 16:21:38 +0100 Alan Gauld wrote:
>
>> In the code below, I can't seem to get around calling an instance
>> method (__reality_check()) from within a class method (update_grid()),
>
> Of course you can't :)
>
> Since the class method has no access to the instance, it cannot call
> instance methods, since it doesn't know which instance to use!
>
> Instead, update_grid needs to be an instance method. Of course it
> does! It needs to update a specific grid, right? That will be the
> instance.

All of a sudden it's clear as day!

>
> Class methods are generally used for alternate constructor functions, e.g.
> dict.fromkeys. Since it makes no difference whether you call fromkeys
> from the class (dict) or from an instance, it is made a classmethod.

...this makes a whole lot of sense now.

>>
>> class Grid:
>>          '''Grid(rows = 26, columns = 26) ->  Provides a world object
>> for the cells in Game of Life'''
>>          from time import sleep
>>          from os import system
>>          from sys import platform
>
> Move the imports outside of the class. They should go at the start of
> the module.
>
> As written, your class has attributes Grid.sleep, Grid.system, etc. Since
> these attributes have nothing to do with Grids, they don't belong in the
> Grid class. Move them out.
>
> Classes are not catch-all collections of unrelated methods. That's
> what modules are for :)

I see, thanks.


>>          @staticmethod
>>          def display_grid(generation, os_type):
>>                  try:
>>                          (lambda x:Grid.system('cls') if 'win' in
>> os_type else Grid.system('clear'))(1)
>
>
> That's wrong! Your display_grid method uses the class (Grid), so it
> shouldn't be a static method.
>
> Move the imports to the module level, and display_grid as a regular
> function:
>
> def display_grid(generation, array):
>      clear_screen()
>      print 'Now in Generation %d' % generation
>      for line in array:
>          print line
>
> def clear_screen(platform=sys.platform()):
>      # The default here is set once, and defaults to your platform.
>      if 'win' in platform:
>          command = 'cls'
>      else:
>          command = 'clear'
>      os.system(command)
>
>
> Or leave display_grid as an instance method:
>
>
> class Grid:
>      def display_grid(self, generation):
>      clear_screen()
>      print 'Now in Generation %d' % generation
>      for line in self._array:
>          print line
>
>
> Either way, clear_screen has nothing to do with grids, so it should
> not be a method of Grid.

After looking at the second option, I thought; Of course! After all
it's "class Grid" so as a "display_grid" instance method makes sense.
I get the logic of it now.

>>                  except:
>>                          print 'Cannot display Grid'
>
> Never use a bare except like that. Bare except should *only* be used
> at the interactive interpreter, for lazy people who want to avoid
> typing. It should never be used non-interactively, it's simply poor
> practice.
>
> The problem with bare excepts is that they cover up too much. Your aim
> is to write code that works. If it has a bug, you need to *see* the bug
> so you can identify it and then fix it. Bare excepts make bugs
> invisible.
>

I admit I used that to avoid dealing with 'exception tantrums' during
debugging, so that the program wouldn't halt if something went wrong
at that point. It was going away anyway. I'll avoid it henceforth.

>>          @staticmethod
>>          def extend_grid(thickness = 4, force_extend = False):
>>                  '''extend_grid([thickness][, force_extend]) -->
>> Extends the edges of the grid by 4 units in
>> all directions if the boundary of the simulation comes close to the
>> edge. Extends anyway if force_extend option is True'''
>
> Why is this a static method?
>
> Which grid is this operating on? It should either take a grid as an
> argument, or extend_grid should be an instance method that operates
> on itself:
>
> # either this top-level function
> def extend_grid(grid, thickness=4, force=False):
>      # do stuff to the grid to extend it
>      # and then return it
>      return grid
>
>
> # or make this a regular instance method
> class Grid:
>      def extend_grid(self, thickness=4, force=False):
>          # do stuff to self to extend it
>          # no need to return anything

Just to clarify, if I went with the top level function; then I guess
I'll define a 'name' attribute for each individual grid and then pass
that name in place of the 'grid' argument in your example. Is this
correct?

>>                  '''Runs the 'Life' World simulation for the specified
>> number of generations at the given speed'''
>>                  #control = input
>>                  #while
>>                  for times in range(generations + 1):
>
> This contains an off-by-one bug. If you pass generations=100 as argument,
> it will run 101 times instead.
>
> Instead, if you want your generations to count from 1:
>
> for times in range(1, generations + 1):
>      ...

thanks, silly mistake


>>          def edit_cell(self, cells, state = '##'):
>>                  '''edit_cell(cells[, state]) -->  Where cells is a list
>> of tuples containing coordinates of the cells to edit'''
>>                  cells = cells
>>                  for eachcell in cells:
>>                          Grid.__array[eachcell[0]][eachcell[1]] = state
>>                  return
>
> This is an abuse of the class. You have instances, but they all share state.
> Why? Sharing state is okay if you have a reason for wanting to share state,
> but here there doesn't appear to be any reason for it.
>
> As you have written this, you cannot have two Grids. Actually you can, but
> since they both share the same state, you cannot have two DIFFERENT Grids.

I don't quite get this one; I intended state to be just the value of
the particular cell in the concerned instance. I don't think it gets
shared by all the grids. The edit method was meant as a way of seeding
the initial cell configuration, so it just writes whatever it is
passed in the 'state' arg over that cell location; then again, I'm
probably missing something here...

> My advice is, forget about class and static methods completely until you
> are a bit more experienced with object-oriented design.
>
most definitely, and to my relief too.
I really appreciate how you walked through the code bit by bit, I've
learned a great deal here.

thank you Steven
---------------------------



On Sun, 14 Oct 2012 16:21:38 +0100 Alan Gauld wrote:
> On 14/10/12 13:34, Osemeka Osuagwu wrote:
>
>> I understand instance, static and class methods but I'm still
>> struggling with when and where they should be used.
>
> You can pretty much ignore static methods, they were almost an
> historic 'accident' superseded, in most cases, by class methods. The
> number of cases where a staticmethod is really needed is very low.
>
> class methods you will rarely need but they are used when you want to do
> something to the entire class of objects. i.e. something that affects
> all instances both existing and future. Or they could be used where
> there are no instances (yet). Factory methods, instance selection
> methods, modifying shared attributes, instance pooling, are all
> examples. To give some kind of heuristic based on my personal usage, I
> use class methods in almost all my bigger projects. Such a project might
> have 20 or 30 (or more) classes. Out of that maybe 2 or 3 will have any
> class methods.
>
> Instance methods are by far the most common. These are the things you
> want to do to a specific instance of the class. Changes to one instance
> have no effect on other instances of the same class.

wonderful explanation; I browsed a lot of material looking for an
explanation like this with real examples and distinction between the
method types. Thanks, it's just what I needed.

>> In the code below, I can't seem to get around calling an instance
>> method (__reality_check()) from within a class method (update_grid()),
>
> It's not a class method, you want to update a specific instance of a
> grid. update_grids() might be a class method if you wanted to, say,
> change the maximum buffer size used to store the grids, or cause all
> grids to display the word 'empty' instead of a blank. Of course to do
> that you would need access to all the instances to update their existing
> values so the constructor would need to store a reference to itself in
> the class somewhere (or maybe in a separate shared database).
> The class method would then iterate over all instances calling their
> instance method update_grid(). This is a common(*) class method pattern,
> where a plural class method calls the singular instance methods
> of all the instances.
>
> (*) I say common, but of course class methods are not common
>      so its a very relative term!

I see. I guess this is like what Steven said in his reply about using
extend_grid as a top level function. I mean storing a reference to
itself and then letting the top level method use that reference to
access the (future) instance(s).

> HTH
> --
> Alan G
> Author of the Learn to Program web site
> http://www.alan-g.me.uk/
>

It sure does. Thank's a great deal.

Abasiemeka.


More information about the Tutor mailing list