469,271 Members | 1,032 Online
Bytes | Developer Community
New Post

Home Posts Topics Members FAQ

Post your question to a community of 469,271 developers. It's quick & easy.

Parameter lists

Consider the following snippet of code:

==========================

class Stats:
def __init__(self, speed, maxHp, armor, strength, attackSpeed, imagePath):
self.speed = speed
self.maxHp = maxHp
self.armor = armor
self.strength = strength
self.attackSpeed = attackSpeed
self.originalImage = loadTexture(imagePath)

==========================

I little container for holding the stats for some rpg character or
something. Now, I dont like the looks of that code, there are many
function parameters to be sent in and if I were to add an attribute, i
would need to add it in three places. Add it to the function
parameters, add it to the class and assign it.

Is there a smoother way to do this? There usually is in python, hehe.
I recall when reading python tutorials that you could do something
like this:

foo(*list_of_parameters):

To send many parameters as a list or a tuple. Then I could assign them
like this:

class Stats:
def __init__(self, *li):
self.speed = li[0]
self.maxHp = li[1]
(...)

Or maybe there is an even niftier way that lets me iterate through
them? Hmm... but that may lead to that I need to store them in a way
that makes it cumbersome to access them later.

Any comments and/or suggestions are welcome! :)
Feb 4 '07 #1
5 1383
Mizipzor
I dont like the looks of that code, there are many
function parameters to be sent in and if I were to add an attribute, i
would need to add it in three places. Add it to the function
parameters, add it to the class and assign it.
Is there a smoother way to do this?
You may use something like this:

def selfassign(self, locals):
# Code from web.py http://webpy.org modified.
for key, value in locals.iteritems():
if key != 'self':
setattr(self, key, value)

Generally used in __init__ methods, as:

def __init__(self, foo, bar, baz=1):
selfassign(self, locals())

You may use it as (untested):

class Stats:
def __init__(self, speed, maxHp, armor, strength, attackSpeed,
imagePath):
selfassign(self, locals())
self.originalImage = loadTexture(imagePath)
del self.imagePath

I don't like that del

Bye,
bearophile

Feb 4 '07 #2
Mizipzor <mi******@gmail.comwrote in
news:ma***************************************@pyt hon.org:
Consider the following snippet of code:

==========================

class Stats:
def __init__(self, speed, maxHp, armor, strength,
attackSpeed, imagePath):
self.speed = speed
self.maxHp = maxHp
self.armor = armor
self.strength = strength
self.attackSpeed = attackSpeed
self.originalImage = loadTexture(imagePath)

==========================

I little container for holding the stats for some rpg character
or something. Now, I dont like the looks of that code, there are
many function parameters to be sent in and if I were to add an
attribute, i would need to add it in three places. Add it to the
function parameters, add it to the class and assign it.

Is there a smoother way to do this? There usually is in python,
hehe. I recall when reading python tutorials that you could do
something like this:

foo(*list_of_parameters):

To send many parameters as a list or a tuple. Then I could
assign them like this:

class Stats:
def __init__(self, *li):
self.speed = li[0]
self.maxHp = li[1]
(...)

Or maybe there is an even niftier way that lets me iterate
through them? Hmm... but that may lead to that I need to store
them in a way that makes it cumbersome to access them later.

Any comments and/or suggestions are welcome! :)
I often use something like this, based on a Martellibot posting:
>>class Stats(dict):
.... def __init__(self, *args, **kwds):
.... self.update(*args)
.... self.update(kwds)
.... def __setitem__(self, key, value):
.... return super(Stats, self).__setitem__(key, value)
.... def __getitem__(self, name):
.... try:
.... return super(Stats, self).__getitem__(name)
.... except KeyError:
.... return None
.... __getattr__ = __getitem__
.... __setattr__ = __setitem__
....
>>m = dict(a=1,b=22,c=(1,2,3))
p = Stats(m,x=4,y=[5,9,11])
p.y
[5, 9, 11]
>>p['y']
[5, 9, 11]

--
rzed
Feb 4 '07 #3
Mizipzor wrote:
class Stats:
def __init__(self, *li):
self.speed = li[0]
self.maxHp = li[1]
(...)

Or maybe there is an even niftier way that lets me iterate through
them?
Using keyword arguments instead of positional parameters makes this easy:
>>class Stats:
.... def __init__(self, **kw):
.... self.__dict__.update(kw)
....
>>stats = Stats(speed=10, maxHp=100, armor='plate mail')
stats.speed
10
>>stats.maxHp
100
>>stats.armor
'plate mail'
Jeffrey
Feb 4 '07 #4
On Sun, 04 Feb 2007 17:45:04 +0100, Mizipzor wrote:
Consider the following snippet of code:

==========================

class Stats:
def __init__(self, speed, maxHp, armor, strength, attackSpeed, imagePath):
self.speed = speed
self.maxHp = maxHp
self.armor = armor
self.strength = strength
self.attackSpeed = attackSpeed
self.originalImage = loadTexture(imagePath)

==========================

I little container for holding the stats for some rpg character or
something. Now, I dont like the looks of that code, there are many
function parameters to be sent in and if I were to add an attribute, i
would need to add it in three places. Add it to the function
parameters, add it to the class and assign it.

Is there a smoother way to do this? There usually is in python, hehe.
There is no "right way" to handle the issue of initialising attributes.
The above way is very common, easy, self-documenting and doesn't have that
many disadvantages unless you have lots of parameters to deal with.

I recall when reading python tutorials that you could do something
like this:

foo(*list_of_parameters):

To send many parameters as a list or a tuple. Then I could assign them
like this:

class Stats:
def __init__(self, *li):
self.speed = li[0]
self.maxHp = li[1]
(...)

That's even worse.

Which is correct?

Stats(..., armour, stealth, ...)
Stats(..., stealth, armour, ...)

You have to read the code to find out. Not just the function definition,
but you actually have to read through all the assignments. Bad bad bad.

Or maybe there is an even niftier way that lets me iterate through
them? Hmm... but that may lead to that I need to store them in a way
that makes it cumbersome to access them later.

def __init__(self, **kwargs):
for key in kwargs:
if hasattr(self, key):
# key clashes with an existing method or attribute
raise ValueError("Attribute clash for '%s'" % key)
self.__dict__.update(kwargs)
=== Advantages ===

(1) you can create new attributes without changing any code;
(2) creating an instance is self-documenting:

Stats(armour="chainmail", stealth=2, strength=4, ...)

(3) attributes can be added in any order;
(4) easy to modify the class so it inherits sensible defaults:

class Stats:
armour = "leather"
wisdom = 10
dexterity = 10
weapon = "sword"
def __init__(self, **kwargs):
for key in kwargs:
if self.__dict__.has_key(key):
raise ValueError("Attribute clash for '%s'" % key
self.__dict__.update(kwargs)

=== Disadvantages ===

(1) You have to put in the attribute name, always:

Stats(armour="chainmail", stealth=2, strength=4, ...) instead of
Stats("chainmail", 2, 4, ...)

(2) Typos can cause strange bugs which are hard to find:

Stats(armour="chainmail", stealth=2, stregnth=4, ...)

Now your character is unexpectedly strong because it inherits the default,
and you don't know why.

(3) Easy to break your class functionality:

Stats(name_that_clashes_with_a_method="something else", ...)

If you've got lots of attributes, you're better off moving them to
something like a INI file and reading from that:

class Stats:
defaults = "C:/path/defaults.ini"
def __init__(self, filename=None, **kwargs):
if not filename:
filename = self.__class__.defaults
self.get_defaults(filename) # an exercise for the reader
for key in kwargs:
if not self.__dict__.has_key(key):
raise ValueError("Unknown attribute '%s' given" % key)
self.__dict__.update(kwargs)

Notice that here I've changed from testing for attributes which clash to
testing for attributes which *don't* match a key in the INI file. Which is
the "best" behaviour, I leave up to you to decide.

--
Steven D'Aprano

Feb 5 '07 #5
Steven D'Aprano a écrit :
On Sun, 04 Feb 2007 17:45:04 +0100, Mizipzor wrote:

>>Consider the following snippet of code:

class Stats:
def __init__(self, speed, maxHp, armor, strength, attackSpeed,
imagePath):
> self.speed = speed
self.maxHp = maxHp
self.armor = armor
self.strength = strength
self.attackSpeed = attackSpeed
self.originalImage = loadTexture(imagePath)
I little container for holding the stats for some rpg character or
something. Now, I dont like the looks of that code, there are many
function parameters to be sent in and if I were to add an attribute, i
would need to add it in three places. Add it to the function
parameters, add it to the class and assign it.

Is there a smoother way to do this? There usually is in python, hehe.

(snip)
>
def __init__(self, **kwargs):
for key in kwargs:
if hasattr(self, key):
# key clashes with an existing method or attribute
raise ValueError("Attribute clash for '%s'" % key)
self.__dict__.update(kwargs)
=== Advantages ===
(snip)
>
=== Disadvantages ===
(snip)
(2) Typos can cause strange bugs which are hard to find:

Stats(armour="chainmail", stealth=2, stregnth=4, ...)

Now your character is unexpectedly strong because it inherits the
default,
and you don't know why.
(3) Easy to break your class functionality:

Stats(name_that_clashes_with_a_method="something else", ...)
How to overcome these two problem - just overcomplexifying things a bit:

class StatsAttribute(object):
def __init__(self, default=None):
self._default = default
self._attrname = None # set by the StatType metaclass

def __get__(self, instance, cls):
if instance is None:
return self
return instance._stats.get(self._attrname, self._default)

def __set__(self, instance, value):
instance._stats[self._attrname] = value

class StatsType(type):
def __init__(cls, name, bases, attribs):
super(StatsType, cls).__init__(name, bases, attribs)
statskeys = getattr(cls, '_statskeys', set())
for name, attrib in attribs.items():
if isinstance(attrib, StatsAttribute):
# sets the name to be used to get/set
# values in the instance's _stats dict.
attrib._attrname = name
# and store it so we know this is
# an expected attribute name
statskeys.add(name)
cls._statskeys = statskeys

class Stats(object):
__metaclass__ = StatsType

def __init__(self, **stats):
self._stats = dict()
for name, value in stats.items():
if name not in self._statskeys:
# fixes disadvantage #2 : we won't have unexpected kw args
msg = "%s() got an unexpected keyword argument '%s'" \
% (self.__class__.__name__, name)
raise TypeError(msg)
setattr(self, name, value)

# just a dummy object, I didn't like the
# idea of using strings litterals for things
# like armors or weapons... And after all,
# it's supposed to be overcomplexified, isn't it ?
class _dummy(object):
def __init__(self, **kw):
self._kw = kw

def __getattr__(self, name):
return self._kw[name]

class Leather(_dummy): pass
class Sword(_dummy): pass
class FullPlate(_dummy): pass
class MagicTwoHanded(_dummy): pass

# let's go:
class Warrior(Stats):
# fixes disatvantage 3 : we won't have name clash
strength = StatsAttribute(default=12)
armour = StatsAttribute(default=Leather())
weapon = StatsAttribute(default=Sword())

bigBill = Warrior(
strength=120,
armour=FullPlate(),
weapon=MagicTwoHanded(bonus=20)
)

try:
wontDo = Warrior(
sex_appeal = None
)
except Exception, e:
print "got : %s" % e
Did I won a MasterProgrammer (or at least a SeasonnedPro) award ?-)
http://sunsite.nus.sg/pub/humour/prog-evolve.html

Err... me go to bed now...
If you've got lots of attributes, you're better off moving them to
something like a INI file and reading from that:

class Stats:
defaults = "C:/path/defaults.ini"
def __init__(self, filename=None, **kwargs):
if not filename:
filename = self.__class__.defaults
self.get_defaults(filename) # an exercise for the reader
for key in kwargs:
if not self.__dict__.has_key(key):
raise ValueError("Unknown attribute '%s' given" % key)
self.__dict__.update(kwargs)
And then allow for Python source code in the INI file (that will be used
to create methods) to specify behaviour ?-)

Ok, this time I really go to bed !-)

Feb 5 '07 #6

This discussion thread is closed

Replies have been disabled for this discussion.

Similar topics

70 posts views Thread by py | last post: by
2 posts views Thread by not_a_commie | last post: by
2 posts views Thread by Kaushal | last post: by
1 post views Thread by CARIGAR | last post: by
By using this site, you agree to our Privacy Policy and Terms of Use.