0

One of my classes does a lot of aggregate calculating on a collection of objects, then assigns an attribute and value appropriate to the specific object: I.e.

class Team(object):
    def __init__(self, name): # updated for typo in code, added self
        self.name = name

class LeagueDetails(object):
    def __init__(self): # added for clarity, corrected another typo
        self.team_list = [Team('name'), ...]
        self.calculate_league_standings() # added for clarity


    def calculate_league_standings(self):
        # calculate standings as a team_place_dict
        for team in self.team_list:
            team.place = team_place_dict[team.name] # a new team attribute

I know, as long as the calculate_league_standings has been run, every team has team.place. What I would like to be able to do is to scan the code for class Team(object) and read all the attributes, both created by class methods and also created by external methods which operate on class objects. I am getting a little sick of typing for p in dir(team): print p just to see what the attribute names are. I could define a bunch of blank attributes in the Team __init__. E.g.

class Team(object):
    def __init__(self, name): # updated for typo in code, added self
        self.name = name
        self.place = None # dummy attribute, but recognizable when the code is scanned

It seems redundant to have calculate_league_standings return team._place and then add

@property
def place(self): return self._place

I know I could comment a list of attributes at the top class Team, which is the obvious solution, but I feel like there has to be a best practice here, something pythonic and elegant here.

7
  • Please, clarify this phrase "then assigns and attribute and value appropriate to". For me it means nothing Commented Mar 3, 2013 at 18:51
  • So if ld = LeagueDetails(), and then ld.calculate_league_standing(). It calculates the place value and assigns that value to the attribute place for each team. When I look at the code for class Team(object), it doesn't show the thing which is obvious .. each team has a team.place attribute. Commented Mar 3, 2013 at 18:57
  • 3
    Your code as written is erroneous, since you used self outside a method. Your line self.team_list = ... won't work. Why don't you make an __init__ method that sets self.team_list and also calls calculate_league_standing? Commented Mar 3, 2013 at 19:07
  • 2
    And there must be parameter selfin the definition of __init__ Commented Mar 3, 2013 at 19:13
  • 1
    You should just use your own example with the "dummy" attribute in it - there's no harm in this and it's the correct way to do this according to coding standards. If you were to run your code through pylint (a program that analyses your code according to standards and looking for errors), you'd get an Warning that the attribute 'place' was defined outside of init (pylint-messages.wikidot.com/messages:w0201). What's the harm in defining it in init anyway? Commented Mar 3, 2013 at 19:53

2 Answers 2

1

If I half understand your question, you want to keep track of which attributes of an instance have been added after initialization. If this is the case, you could use something like this:

#! /usr/bin/python3.2

def trackable (cls):
    cls._tracked = {}

    oSetter = cls.__setattr__
    def setter (self, k, v):
        try: self.initialized
        except: return oSetter (self, k, v)
        try: self.k
        except:
            if not self in self.__class__._tracked:
                self.__class__._tracked [self] = []
            self.__class__._tracked [self].append (k)
        return oSetter (self, k, v)
    cls.__setattr__ = setter

    oInit = cls.__init__
    def init (self, *args, **kwargs):
        o = oInit (self, *args, **kwargs)
        self.initialized = 42
        return o
    cls.__init__ = init

    oGetter = cls.__getattribute__
    def getter (self, k):
        if k == 'tracked': return self.__class__._tracked [self]
        return oGetter (self, k)
    cls.__getattribute__ = getter

    return cls

@trackable
class Team:
    def __init__ (self, name, region):
        self.name = name
        self.region = region

#set name and region during initialization
t = Team ('A', 'EU')

#set rank and ELO outside (hence trackable)
#in your "aggregate" functions
t.rank = 4 # a new team attribute
t.ELO = 14 # a new team attribute

#see witch attributes have been created after initialization
print (t.tracked)

If I did not understand the question, please do specify which part I got wrong.

Sign up to request clarification or add additional context in comments.

2 Comments

This code creates all sorts of ideas which I really appreciate. When I assign attributes outside of __init__(self), I do so only for the purpose of being able to display the results. Thanks!
If you could elaborate a wee more on what are the actual requirements, I would be glad to help more precisely.
1

Due to Python's dynamic nature, I don't believe there is a general answer to your question. An attribute of an instance can be set in many ways, including pure assignment, setattr(), and writes to __dict__ . Writing a tool to statically analyze Python code and correctly determine all possible attributes of an class by analyzing all these methods would be very difficult.

In your specific case, as the programmer you know that class Team will have a place attribute in many instances, so you can decide to be explicit and write its constructor like so:

class Team(object):
def __init__(name ,place=None):
    self.name = name
    self.place = place

I would say there is no need to define a property of a simple attribute, unless you wanted side effects or derivations to happen at read or write time.

2 Comments

My question is all about readability, and your answer reflects my general leaning. I didn't want to write a tool, but rather write better code. I was grow concerned that assigning a long list of attributes to a class object was bad style because those attributes end up as kind of a mystery meat <- technical term. You can't just go to the class code to remind yourself of the full list of expected expected attributes.
I selected the other answer because of the value it creates for the actual project I am working on. Your answer, though, was just the type of discussion I wanted.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.