Call Python Method on Class Attribute Change - python

I'm writing an API parsing Twitter bot and am very new to OOP. I have some existing Python code that relies on global variables and figured I could take this opportunity to learn.
I have the following Team class that gets updated when the API is parsed and is like to be able to call a totally unrelated (external) method when a class attribute changes.
class Team(object):
def __init__(self, team_name, tri_code, goals, shots, goalie_pulled):
self.team_name = team_name
self.tri_code = tri_code
self.goals = goals
self.shots = shots
self.goalie_pulled = goalie_pulled
When goalie_pulled is changed for an existing instance of Team I'd like the following method to be called (pseudo code):
def goalie_pulled_tweet(team):
tweet = "{} has pulled their goalie with {} remaining!".format(team.team_name, game.period_remain)
send_tweet(tweet)
Two things -
How do I call goalie_pulled_tweet from within my Team class once I detect that goalie_pulled attribute has changed?
Can I access an instance of my Game object from anywhere or does it need to be passed to that variable as well?

You should take a look at the property class. Basically, it lets you encapsulate behaviour and private members without the consumer even noticing it.
In your example, you may have a goalie_pulled property:
class Team(object):
def __init__(self, team_name, tri_code, goals, shots, goalie_pulled):
# Notice the identation here. This is very important.
self.team_name = team_name
self.tri_code = tri_code
self.goals = goals
self.shots = shots
# Prefix your field with an underscore, this is Python standard way for defining private members
self._goalie_pulled = goalie_pulled
#property
def goalie_pulled(self):
return self._goalie_pulled
#goalie_pulled.setter
def goalie_pulled(self, new_value):
self._goalie_pulled = new_value
goalie_pulled_tweet(self) #self is the current Team instance
From the consumer's point of view:
team = create_team_instance()
# goalie_pulled_tweet is called
team.goalie_pulled = 'some_value'
I'd recommend you to use properties whenever you can (and must), as they are a nice way of abstraction.

From a design standpoint, it would make more sense to have a pull_goalie method.
Classes are a tool to create more meaningful abstractions. Pulling a goalie is an action. If you think of Team as representing a real-life team, it makes more sense to say "The team pulled their goalie!" rather than "The team set their pulled-goalie attribute to X player!"
class Team(object):
...
def pull_goalie(self, player):
self.pulled_goalie = player
tweet = '<your format string>'.format(
self.pulled_goalie,
# Yes, your Team *could* store a reference to the current game.
# It's hard to tell if that makes sense in your program without more context.
self.game.period_remaining
)
I was going to recommend a property, but I think that would solve the immediate problem without considering broader design clarity.
NOTE: There is no such thing as a "private" attribute in Python. There is a convention that attributes beginning with a single underscore (self._pulled_goalie) is treated as private, but that's just so that people using your code know that they can't depend on that value always doing what they think it will. (i.e., it's not part of the public contract of your code, and you can change it without warning.)
EDIT: To create a register_team method on a Game object, you might do something like this:
class Game(object):
def __init__(<stuff>):
...
self.teams = {}
...
def register_team(self, team):
if len(self.teams) > 1:
raise ValueError(
"Too many teams! Cannot register {} for {}"
.format(team, game)
)
self.teams[team] = team
team.game = self
def unregister_team(self, team):
try:
del self.teams[team]
except KeyError:
pass
team.game = None
Note that by using a dictionary, register_team and unregiser_team can be called multiple times without ill effect.

Related

Design pattern to allow attaching one class to another

I am designing an RPG and would like to have the ability to attach classes to each other. What I'm looking to do is have say an Item class. The weapon class would inherit from it. A sword would be an instance of the weapon class. I want to then be able to attach properties to the sword. These properties would be other classes. For example I could attach the container class to it and the sword (only that instance of the sword) would become a container. I could also maybe attach something like an enchantment to that sword.
For a bonus it would be nice to be able to combine instances as well. So instead of having to have a fire_enchantment class I could just make it an instance of Enchantment and attach it to the sword instance.
I've googled around and haven't been able to find a design pattern that fits this. I recall studying one but can't remember what it was called (Was a few years ago)
I'm at a loss of of which design pattern allowed this. The combining of multiple classes dynamically.
I think you seem to understand the idea of inheritance in python (e.g. class Subclass(Superclass): ) so I won't cover that here.
The classes you want to 'attach' can be treated as any other variable within the Weapon class.
class Enchantment(object):
def __init__(self, name, type):
self.name = name
self.type = type
# can define more member variables here, and set with setter methods
# more Enchantment methods here...
class Weapon(object):
def __init__(self, name, type)
self.name = name
self.type = type
self.enchantments = []
# more Weapon member variables here
def add_enchantment(self, enchantment):
# any logic you need to check when adding an enchantment
self.enchantments.append(enchantment)
Then in wherever your game code is running you could do
sword = Weapon('My sword', 'sword')
fire_enchantment = Enchantment('Fireball', 'fire')
sword.add_enchantment(fire_enchantment)
You can then add methods on the Weapon class to do things with the enchantments/add certain logic.
The enchantment is still an instance of an object, so if you access it in the list (maybe by identifying it by its name, or looping through the list) all its methods and variables are accessible. You just need to build an interface to it via the Weapon class e.g. get_enchantment(self, name), or have other methods in the Weapon class interact with it (e.g. when you attack you might loop through the enchantments and see if they add any extra damage).
There's obviously design considerations about how you design your classes (the above was thrown together for example and doesn't include inheritance). For example you might only allow one enchantment per weapon, in which case you shouldn't use a list in the weapon object, but could just set self.enchantment = None in the constructor, and set self.enchantment = enchantment in the add_enchantment method.
The point I'm making is you can treat instances of Enchantment or other 'attachable' classes as variables. Just make sure you create an instance of the class e.g. fire_enchantment = Enchantment('Fireball', 'fire').
There's plenty of reading out there in terms of inheritance and OOP in general. Hope this helps!
Additional answer from OP
I think the Mixin pattern is what I was looking for. After digging around more I found this post which has an answer for dynamic mixin's.

Edit parent object's attributes in a nested object

I am trying to solve a problem where I have a class which is used to store objects of other classes. I want to be able to create a list in that parent object of certain types of nested objects. (sorry if I'm phrasing this badly)
For my example I am taking a real world problem of a House. In the house we may have many different 'things'. We could have people, pets, furniture, computers etc.
I want to be able to store this in a nested format so I create a House with any of those sub-objects within.
E.g
old_rectory = House(
Person('Barry'),
Person('Bob'),
Pet('Bill'),
Pet('Brenda')
)
I have created 3 classes: the first is for the House itself, then I have a class for people and a class for pets.
For the house class I use *argv so that I can add as many other objects as necessary.
Now in the example above I would like to be able to access old_rectory.people and see a list of any instances in old_rectory that are of the type Person.
class House:
def __init__(self,*argv):
self.people = []
self.pets = []
for arg in argv:
if isinstance(arg, Person):
self.people.append(arg)
elif isinstance(arg, Pet):
self.pets.append(arg)
class Person:
def __init__(self,name):
self.name = name
class Pet:
def __init__(self,name):
self.name = name
You can see that I have achieved this by hard coding the lists and some if conditions into the House class. But this means that whenever I create a new class I also need to add in a new list and the logic to the House class.
I would like to be able to access the house object's attributes from the individual classes (Person, Pet, etc.) but I am unsure of how to proceed or if it is even possible.
class Doors:
NOW CREATE THE NEW LIST IN HOUSE CLASS
def __init__(self,name):
self.name = name
NOW APPEND THIS OBJECT TO THE NEW LIST IN THE HOUSE CLASS
I can see two clear patterns on how to try to overcome this:
Create methods for getting the instances of a specific class
Simply put every instance inside a big list and add methods to get each "sublist":
class House:
def __init__(self,*argv):
self.things = [*argv]
#property
def pets(self):
return [thing for thing in self.things if isinstance(thing, Pet)]
#property
def people(self):
return [thing for thing in self.things if isinstance(thing, Person)]
This doesn't really solve your initial problem, but at least it's easier and cleaner to implement for new classes - if a list attribute does not exist, it's because you haven't implemented the method for it.
Use hasattr, setattr and getattr
Use these functions on the __init__ method to programatically check if each list exists, create them if needed and append each instance to the corresponding list:
class House:
def __init__(self,*argv):
for arg in argv:
name = arg.__class__.__name__.lower()
if not hasattr(self, name):
setattr(self, name, [])
getattr(self, name).append(arg)
I personally think this is worse, since your attributes will be named exactly like the class name (i.e. person, not people), and you can't clearly see which lists are initialized as attributes or not since it's done on the fly, but it should work for your described use case.
Whichever way you decide to go with, note that I personally feel like your design isn't very effective for dealing with this problem. I'd rather create empty lists for people, pets etc on the House's __init__ method and add specific methods like add_person, add_pet, add_group etc for appending objects to the House's list attributes. It may not seem like much, but this design:
a) clearly defines supported classes that can interact with the House class; and
b) lets you see more clearly exactly who is getting put into the House, since you need to explicitly call the method in order to do so.
I have restructured your code. Check it out :)
class House:
def __init__(self,*argv):
self.house_classes = {"people": Person, "pets": Pet}
self.house_objects = {}
for object in argv:
self.add_house_object(object)
def add_house_class(self, class_id, class):
self.house_classes["class_id"] = class
def add_house_object(self, object):
for class_id in self.house_classes:
if isinstance(object, self.house_classes[class_id]):
if class_id in self.house_objects:
self.house_objects["class_name"].append(object)
return
self.house_objects["class_id"] = [object]
class Person:
def __init__(self,name):
self.name = name
class Pet:
def __init__(self,name):
self.name = name
To add new classes (e.g Doors) to a house object (as i think you want)
my_house = House(house_objects...) #initialise a house object
class Doors: #your new class to add to house object
def __init__(self,name):
self.name = name
my_house.add_house_class(self, "doors", Doors) #added to house object
new_door = Door("my door") #door object
my_house.add_house_object(new_door)
I hope that helps :)
You can check if House has a doors list with getattr(House, 'doors', None) and create the list if it is not existing. This solution assumes that you intend to create the list as a class variable (I am assuming this, since you do NOT pass any House-instance reference do Doors when instantiating a Doors instance).
class Doors:
def __init__(self,name):
if getattr(House, 'doors', None) is None:
House.doors = []
self.name = name
House.doors.append(self)
BUT I strongly advise you to NOT USE THIS PATTERN. This looks like a good case for class inheritance, for example creating the class Doors with class Doors(House):.
Furthermore I've got the feeling that you should take a look at the definitions and meanings of class variables and instance variables.
Imho the best way to deal with this task would be to make Doors a class which inherits from House and to require an existing instance of house to be able to create a Doors instance (for example check with if isinstance(new_house, House):). Then the Doors __init__ method could create and/or append a list doors as instance variable to House.
This way you can create many different houses. Whereas when using a class variable for the doors list, each house would have all doors created in all houses.
Thus I recommend using this pattern:
class Doors(House):
def __init__(self, name, house):
if not isinstance(house, House):
raise ValueError('`house` is not an existing instance of the `House` class')
if getattr(house, 'doors', None) is None:
house.doors = []
else:
print('exi')
self.name = name
house.doors.append(self)
As jfaccioni pointed out: Inheritance is not mandatory here, but this kind of construct looks like you are going to need it in the long term for method-access etc.

How do you keep code consistent with multiple developers?

I know that Python is a dynamically typed language, and that I am likely trying to recreate Java behavior here. However, I have a team of people working on this code base, and my goal with the code is to ensure that they are doing things in a consistent manner. Let me give an example:
class Company:
def __init__(self, j):
self.locations = []
When they instantiate a Company object, an empty list that holds locations is created. Now, with Python anything can be added to the list. However, I would like for this list to only contain Location objects:
class Location:
def __init__(self, j):
self.address = None
self.city = None
self.state = None
self.zip = None
I'm doing this with classes so that the code is self documenting. In other words, "location has only these attributes". My goal is that they do this:
c = Company()
l = Location()
l.city = "New York"
c.locations.append(l)
Unfortunately, nothing is stopping them from simply doing c.locations.append("foo"), and nothing indicates to them that c.locations should be a list of Location objects.
What is the Pythonic way to enforce consistency when working with a team of developers?
An OOP solution is to make sure the users of your class' API do not have to interact directly with your instance attributes.
Methods
One approach is to implement methods which encapsulate the logic of adding a location.
Example
class Company:
def __init__(self, j):
self.locations = []
def add_location(self, location):
if isinstance(location, Location):
self.locations.append(location)
else:
raise TypeError("argument 'location' should be a Location object")
Properties
Another OOP concept you can use is a property. Properties are a simple way to define getter and setters for your instance attributes.
Example
Suppose we want to enforce a certain format for a Location.zip attribute
class Location:
def __init__(self):
self._zip = None
#property
def zip(self):
return self._zip
#zip.setter
def zip(self, value):
if some_condition_on_value:
self._zip = value
else:
raise ValueError('Incorrect format')
#zip.deleter
def zip(self):
self._zip = None
Notice that the attribute Location()._zip is still accessible and writable. While the underscore denotes what should be a private attribute, nothing is really private in Python.
Final word
Due to Python's high introspection capabilities, nothing will ever be totally safe. You will have to sit down with your team and discuss the tools and practice you want to adopt.
Nothing is really private in python. No class or class instance can
keep you away from all what's inside (this makes introspection
possible and powerful). Python trusts you. It says "hey, if you want
to go poking around in dark places, I'm gonna trust that you've got a
good reason and you're not making trouble."
After all, we're all consenting adults here.
--- Karl Fast
You could also define a new class ListOfLocations that make the safety checks. Something like this
class ListOfLocations(list):
def append(self,l):
if not isinstance(l, Location): raise TypeError("Location required here")
else: super().append(l)

Attribute name change for inherited classes. Possible/Bad practice?

Q1. If I have a very general class, with an attribute whose name could be better represented in more specific inherited classes, how can I access the same methods from the parent class if the attribute has changed its name? For example (not my real scenario, but it shows what I mean).
class Entity(object):
def __init__(self):
self.members= {}
... # Methods that use self.members
class School(Entity):
def __init__(self):
super(Entity,self).__init__(self)
class Company(Entity):
def __init__(self):
super(Entity,self).__init__(self)
for class School and for class Company, I would like to be able to use attributes that are more specific, such as self.students and self.employees, but that still work with the methods that were defined for self.members in the class Entity.
Q2. Would this be bad practice? What would be the best way to approach this? In my real case, the word I used for self.members is too general.
Renaming an attribute in a subclass is bad practice in general.
The reason is that inheritance is about substitutability. What it means for a School to be an Entity is that you can use a School in any code that was written to expect an Entity and it will work properly.
For example, typical code using an Entity might do something like this:
for member in entity.members:
If you have something that claims to be an Entity (and even passes isinstance(myschool, Entity)), but it either doesn't have members, or has an empty members, because its actual members are stored in some other attribute, then that code is broken.
More generally, if you change the interface (the set of public methods and attributes) between a base class and dericed class, the derived class isn't a subtype, which means it usually shouldn't be using inheritance in the first case.1
If you make students into an alias for members, so the same attribute can be accessed under either name, then you do have a subtype: a School has its students as members, and therefore it can be sensibly used with code that expects an Entity:
myschool.students.append(Person(cap'))
# ...
for member in myschool.members:
# now cap is going to show up here
And this works just as well with methods defined in Entity:
def slap_everyone(self):
for member in self.members:
# this will include cap
member.slap()
myschool.slap_everyone()
And you can do this by using #property.
class Student(Entity):
# ...
#property
def students(self):
return members
#students.setter
def students(self, val):
self.members = val
#students.deleter
def students(self):
del self.members
So, this isn't flat-out invalid or anything.
But it is potentially misleading.
Will it be obvious to readers of your code that adding cap to myschool.students is going to add him to myschool.members? If so, it's probably OK. If not, or if you're not sure, then you probably shouldn't do this.
Another thing to consider is that a School might have multiple kinds of members: students, teachers, administrators, dropouts who hang around their old campus because they don't know where else to find drug dealers, … If that's part of your design, then what you really want is for members to be a property, and probably a read-only property at that,2 and each subclass can define what counts as "members" in a way that makes sense for that subclass.
class Entity(object):
#property
def members(self):
return []
def rollcall(self):
return ', '.join(self.members)
class School(Entity):
def __init__(self):
super(School, self).__init__()
self.students, self.teachers = [], []
#property
def members(self):
return self.students + self.teachers
school = School()
school.teachers.append('cap')
school.students.extend(['marvel', 'america, planet'])
print(school.rollcall())
This will print out:
cap, marvel, america, planet
That school is working as a School, and as an Entity, and everything is good.
1. I say usually because (regardless of what OO dogma says) there are other reasons for subclassing besides subtyping. But it's still the main reason. And in this case, there doesn't appear to be any other reason for subclassing—you're not trying to share storage details, or provide overriding hooks, or anything like that.
2. In fact, you might even want to drag in the abc module and make it an abstract property… but I won't show that here.

Avoid putting "global" in every single function

Python is supposed to be fun, simple and easy to learn.
Instead, it's been a huge pain.
I've discovered that all the errors I'm getting are related to me not declaring each variable global in each function.
So for my toy program of dressUp, I have to write:
hatColor = ""
shirtColor = ""
pantsColor = ""
def pickWardrobe(hat, shirt, pants):
global hatColor
global shirtColor
global pantsColor
...
This gets really annoying when I have 20 functions, and each one needs to have 20 global declarations at the beginning.
Is there any way to avoid this?
Thanks!
ADDED
I am getting tons of `UnboundLocalError - local variable X referenced before assignment.
Why am I doing this? Because I need to write a py file that can do some calculations for me. I don't want it all in the same function, or it gets messy and I can't reuse code. But if I split the work among a few functions, I have to declare these annoying globals over and over.
Classes versus global variables
global is common to all
class is a template for an object, representing something, here it could be person dressed up somehow.
Class might have class properties, these are not so commonly used, as they are shared by all instances (sort of "global for classes).
Classes start living as soon as you instantiate them, it means, the pattern defined by class definition is realized in form of unique object.
Such an object, called instance, might have it's own properties, which are not shared with other instances.
I am sometime thinking about a class as of a can - class definition means "can is something you can put thing into" and instance is real tangible can, which has a name of it and in Python I put property values into it, which are bound to the name of given can holder.
DressUp class with real instance properties
Properties in "holmeswatson" solution are bound to class definition. You would run into problems if you would use multiple instances of DressUp, they would be sharing the properties over class definition.
It is better and safer to use it as instance variables, which are over self bound to instance of the class, not to class definition.
Modified code:
class DressUp:
def __init__(self, name, hatColor="", shirtColor=""):
self.name = name
self.hatColor = hatColor
self.shirtColor = shirtColor
def pickWardrobe(self,hat, shirt):
self.hatColor = hat
self.shirtColor = shirt
def __repr__(self):
name = self.name
hatColor = self.hatColor
shirtColor = self.shirtColor
templ = "<Person:{name}: hat:{hatColor}, shirt:{shirtColor}>"
return templ.format(name=name, hatColor=hatColor, shirtColor=shirtColor)
tom = DressUp("Tom")
tom.pickWardrobe("red","yellow")
print "tom's hat is", tom.hatColor
print "simple print:", tom
print "__repr__ call:", tom.__repr__()
jane = DressUp("Jane")
jane.pickWardrobe("pink","pink")
print "jane's hat is", jane.hatColor
print "simple print:", jane
print "__repr__ call:", jane.__repr__()
The __repr__ method is used at the moment, you call print tom or print jane.
It is used here to show, how to instance method can get access to instance properties.
Is there any way around it? Yes, there are several. If you're using global variables on a regular basis, you're making a mistake in your design.
One common pattern when you have many functions that will operate on the same, related data is to create a class and then declare instances of that class. Each instance has its own set of data and methods, and the methods within that instance can operate on the data within that instance.
This is called object oriented programming, it is a common and basic paradigm in modern programming.
Several respondents have sketched out what a class might look like in your case but I don't think you've given enough information (which would include the method signatures of the other functions) to actually write out what you need. If you post more information you might get some better examples.
If it is appropriate, you could use classes.
class DressUp:
def __init__(self, name):
self.name = name
def pickWardrobe(self,hat, shirt, pants):
self.hatColor = hat
self.shirtColor = shirt
self.pantsColor = pants
obj1 = DressUp("Tom")
obj1.pickWardrobe("red","yellow","blue")
print obj1.hatColor
Have a look:
http://www.tutorialspoint.com/python/python_classes_objects.htm

Categories

Resources