osdir.com


[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Design: method in class or general function?


leam hall wrote:

> On Thu, Sep 7, 2017 at 8:16 AM, Steve D'Aprano
> <steve+python at pearwood.info> wrote:
> 
>> On Thu, 7 Sep 2017 07:20 pm, Leam Hall wrote:
>>
>> > OOP newbie on Python 2.6.
>>
>> Python 2.6 is ancient, and is missing many nice features. You should
>> consider
>> using the latest version, 3.6.
>>
> 
> I've wrestled with that discussion for a while and Python 3 loses every
> time. There's literally no good reason for me to move to Python 3 earlier
> than mid-2020's. Please accept the fact that there are hundreds of
> thousands of servers, if not millions, running Python 2.x. Whether or not
> Python 3 has any neat cool stuff is irrelevant to those of us seeking to
> use Python to get today's work done.
> 
>> I create instances of Character class with an attribute dict of
>> > 'skills'. The 'skills' dict has the name of a skill as the key and an
>> > int as a value. The code adds or modifies skills before outputting the
>> > Character.
>> >
>> > Is it better design to have a Character.method that takes a 'skill' key
>> > and optional value or to have a general function that takes an
>> > instance, a dict, a key, and an optional value?
>>
>> I'm afraid your example is too generic for me to give an opinion. Do you
>> literally mean a method called "method"? What does it do?
>>
> 
> 
> Using this:
> https://github.com/makhidkarun/py_tools/blob/master/lib/character.py
> 
> Line 19 sets "self.skills" either from the passed in data or from
> https://github.com/makhidkarun/py_tools/blob/master/lib/character_tools.py
> #L34-L48
> 
> So Character.skills is a dict with a string key and an int value. I need
> to be able to add skills and my first attempt is a function:
> https://github.com/makhidkarun/py_tools/blob/master/lib/character_tools.py
> #L52-L56
> 
> Should the "add_skills" function be a method in the character class or be
> made a more generic function to add/modify a key/value pair in a dict that
> is an attribute of an instance? Other tasks will require the add/modify
> functionality but coding that increases complexity. At least for me,
> anyway.
> 
> Sorry about being unclear earlier, coffee was still kicking in and I'm
> still a newbie that mixes up terms.

I'm pleading "method" as it allows per-class implementation. 

Say you use per-career subclasses of a general Character class. There are 
default per-career skill sets, but usually a Character can acquire a skill 
that is not needed in his career -- with the exception that Rogues cannot 
tap dance ;)

Below is a way to implement that with a specialised add_skill() method:

$ cat basic_oo.py    
from __future__ import print_function
import random
from collections import defaultdict


class Character(object):
    DEFAULT_SKILLS = ['Blade', 'GunCbt', 'Admin', 'Streetwise']

    def __init__(self):
        self.skills = defaultdict(int)

    def add_random_skills(self, terms):
        skillnames = self.DEFAULT_SKILLS
        for _ in range(2*terms):
            self.add_skill(random.choice(skillnames))

    def add_skill(self, name, amount=1):
        self.skills[name] += amount

    def __str__(self):
        skills = ", ".join(
            "{}={}".format(name, amount)
            for name, amount in sorted(self.skills.items())
            if amount != 0
        )
        return "{}({})".format(self.__class__.__name__, skills)


class Rogue(Character):
    def add_skill(self, name, amount=1):
        if name == "TapDance":
            raise ValueError("Sorry, this rogue will never tap dance")
        super(Rogue, self).add_skill(name, amount)


class Marine(Character):
    DEFAULT_SKILLS = ['GunCbt', 'VaccSuit', 'Leadership', 'Vehicle']


def main():
    NUM_CHARACTERS = 5
    CHARACTERS = [Marine, Rogue]

    characters = [
        random.choice(CHARACTERS)() for _ in range(NUM_CHARACTERS)
    ]

    for c in characters:
        c.add_random_skills(5)
        c.add_skill("RepairBicycles", random.randrange(3))
        try:
            c.add_skill("TapDance", 3)
        except ValueError as err:
            print(err)

    for c in characters:
        print(c)


if __name__ == "__main__":
    main()

$ python basic_oo.py 
Sorry, this rogue will never tap dance
Sorry, this rogue will never tap dance
Sorry, this rogue will never tap dance
Rogue(Admin=3, Blade=4, GunCbt=2, Streetwise=1)
Marine(GunCbt=5, Leadership=4, TapDance=3, VaccSuit=1)
Rogue(Blade=3, GunCbt=2, RepairBicycles=2, Streetwise=5)
Rogue(Admin=1, Blade=2, GunCbt=5, RepairBicycles=1, Streetwise=2)
Marine(GunCbt=1, Leadership=3, RepairBicycles=2, TapDance=3, VaccSuit=2,
Vehicle=4)