I'm trying to create a way to apply a prefix to an item which would modify the item's existing stats. For example in the code below I am trying to apply the 'huge' prefix to the 'jar' item. I'd like to make the code reusable so that I could have different prefixes ('fast', 'healthy') that would modify different item stats.
Is it possible to hold the name of a class member in a variable?
If so, is there any reason I shouldn't?
If not, what alternatives are there?
class Prefix(object):
def __init__(self, word, stat, valu):
self.word = word
self.stat = stat
self.valu = valu
class Item(object):
def __init__(self, name, size):
self.name = name
self.size = size
def apply_prefix(self, prefix):
self.prefix.stat += prefix.valu # <-- Here is my issue
self.name = prefix.word + ' ' + self.name
# My hope is to make the code reusable for any stat
def print_stats(self):
print self.name, self.size
def main():
jar = Item('jar', 10)
huge_prefix = Prefix('huge', 'size', 5)
jar.apply_prefix(huge_prefix)
jar.print_stats()
You're trying to dynamically refer to some attribute. You do that by using getattr. And if you want to set the attribute, well... that's setattr :)
def apply_prefix(self, prefix):
target_attr = getattr(self,prefix.stat) #dynamically gets attr
setattr(self,prefix.stat,target_attr+prefix.valu)
As to whether this is the best coding style: it depends. There are some instances that code is made more clear by use of getattr. Since right now you only have two stats, it seems excessive to need this kind of dynamic attribute referencing, since I could easily do:
bogus_prefix = Prefix('huge','bogus',3)
Which is a valid Prefix, but throws an AttributeError when I try to apply it. That's not the most straightforward thing to debug.
However, there are bonuses to the getattr approach: if you add more stats, you don't have to change a bit (haha) of code in Prefix.
Other alternatives? There are always options in Python. :-)
The way I'd do it is to make Prefix just a dict of word:value pairs. Then apply_prefix would loop over the word keys, updating as many values as I wanted in one shot. It's a similarly dynamic approach, but a bit more scalable.
Related
I have a problem with naming two similar methods. One is a static method and another one is a method that is the same method but works on the instance. Is it a proper way to do it or should I use only a static method.
Class GameBoard()
def __init__(self, blank_board):
self.board = blank_board
#staticmethod
def get_empty_cells(board):
"""Returns a list of empty cells coordinates (x,y)"""
empty = []
for row_no, row in enumerate(board):
for cell_no, cell in enumerate(row):
if cell == ' ':
empty.append((row_no, cell_no))
return empty
def board_empty_cells(self):
return self.get_empty_cells(self.board)
board1 = GameBoard(blank_board)
board2 = [.....]
empty_board1 = board1.board_empty_cells()
empty_board2 = GameBoard.get_empty_cells(board2)
The reason of that is that I would like to be able to evaluate other boards with the static method, but also if I want to get the instance's empty cells I would like to call board_empty_cells().
Is that a clean code approach or should I get empty cells like:
board1 = GameBoard(blank_board)
empty_board1 = board1.get_empty_cells(board1.board)
What would be proper names for those two functions to be descriptive and unambiguous that one of them is a static method and another operates on instance. Is there any convention to follow to distinguish static methods from methods?
#staticmethod
def get_empty_cells(board):
pass
def board_empty_cells(self):
pass
A lot of times I run into a 'problem' with proper naming for methods and functions.
Is there any guide/convention how to properly name methods (like get_board, is_finished etc.)? I don't mean PEP 8 which I'm familiar with. I mean something that would help me choose proper names that actually would make my code more readable.
I have a question on the usage of the setattr method in python.
I have a python class with around 20 attributes, which can be initialized in the below manner:
class SomeClass():
def __init__(self, pd_df_row): # pd_df_row is one row from a dataframe
#initialize some attributes (attribute_A to attribute_Z) in a similar manner
if 'column_A' in pd_df_row.columns:
self.attribute_A = pd_df_row['column_A']
else:
self.attribute_A = np.nan
....
if 'column_Z' in pd_df_row.columns:
self.attribute_Z = pd_df_row['column_Z']
else:
self.attribute_Z = np.nan
# initialize some other attributes based on some other columns in pd_df_row
self.other_attribute = pre_process(pd_df_row['column_123'])
# some other methods
def compute_something(self):
return self.attribute_A + self.attribute_B
Is it advisable to write the class in the below way instead, making use of the setattr method and for loop in python:
class SomeClass():
# create a static list to store the mapping between attribute names and column names that can be initialized using a similar logic.
# However, the mapping would not cover all columns in the input pd_df_row or cover all attributes of the class, because not all columns are read and stored in the same way
# (this mapping will be hardcoded. Its initialization cannot be further simplified using a loop, because the attribute name and the corresponding column name do not actually follow any particular patterns)
ATTR_LIST = [('attribute_A', 'column_A'), ('attribute_B', 'column_B'), ...,('attribute_Z', 'column_Z')]
def __init__(self, pd_df_row): #where pd_df_row is a dataframe
#initialize some attributes (attribute_A to attribute_Z) in a loop
for attr_name, col_name in SomeClass.ATTR_LIST:
if col_name in pd_df_row.columns:
setattr(self, attr_name, pd_df_row[col_name])
else:
setattr(self, attr_name, np.nan)
# initialize some other attributes based on some other columns in pd_df_row
self.other_attribute = pre_process(pd_df_row['column_123'])
# some other methods
def compute_something(self):
return self.attribute_A + self.attribute_B
the second way of writing this class seem to be able to shorten the code. However, it also seem to make the structure of the class a bit confusing, by creating the static list of attribute and column name mapping (which will be used to initiate only some but not all of the attributes). Also, I noticed that code auto-completion will not work for the second piece of code as the code editor wont be able to know what attribute is created until run time. Therefore my question is, is it advisable to use setattr() in this way? In what cases should I write my code in this way and in what cases I should avoid doing so?
In addition, does creating the static mapping in the class violate object oriented programming principles? should I create and store this mapping in some other place instead?
Thank you.
You could, but I would consider having a dict of attributes rather than separate similarly named attributes.
class SomeClass():
def __init__(self, pd_df_row): # pd_df_row is one row from a dataframe
self.attributes = {}
for x in ['A', ..., 'Z']:
column = f'column_{x}'
if column in pd_df_row:
self.attributes[x] = pd_df_row[column]
else:
self.attributes[x] = np.nan
# initialize some other attributes
self.other_attribute = some_other_values
# some other methods
def compute_something(self):
return self.attribute['A'] + self.attribute['B']
I have a situation where I need to create a dictionary that keeps track of global order of the values. I haven't been able to find a good way for the class itself to have an incrementing counter that's also tracked by the value.
Here's what I've written in the meanwhile to get around this:
from collections import defaultdict
class NotMyDict(object):
""" defaultdict(list) that tracks order globally across the dict.
Will function as a normal defaultdict(list) unless you modify the
'ordered' attribute and set it to a non-false evaluating value. This
"""
ordered = False
_data = defaultdict(NotMyDictList)
_next_index = 0
class NotMyDictList(list):
def append(self, value):
def __repr__(self):
if self.ordered:
return repr(self._data)
else:
temp = defaultdict(list)
for key in self._data:
for value in self._data[key]:
temp[key].append(value[0])
return repr(temp)
def __getitem__(self, key):
if self.ordered:
return self._data[key]
else:
return [val[0] for val in self._data[key]]
def add_value_to_key(self, key, value):
self._data[key].append((value, self._next_index))
self._next_index += 1
So I can use this like a normal dictionary for pulling values. I could have instantiated a list if the key didn't exist, but defaultdict was simple and easy.
Here's an example of the use:
test = NotMyDict()
test.add_value_to_key('test', 'hi')
test.add_value_to_key('test', 'there')
test.add_value_to_key('test', 'buddy')
test['test']
Result:
['hi', 'there', 'buddy']
test.ordered = True
test['test']
Result:
[('hi', 0), ('there', 1), ('buddy', 2)]
Now - the example of use isn't super important, but the functionality that I can't seem to figure out, is instead of using the .add_value_to_key(), I want to be able to use a normal defaultdict(list) convention of:
dict[key].append()
and still have it track the index. Do I need to pass global object locations with id() and reference those objects at a memory level, or is there a way I just don't understand to have a "class global" that's accessible by it's members?
I had also tried to use nested classes, but the nested class didn't have access to the parent class's globals, so I'd have to:
Make a list-like class that references the parent class attribute somehow (Maybe with id() and direct memory location reference?)
modify/make it's append() function so that it also updates the parent class global counter, and tracks the value with this counter as a metadata field.
I really just can't seem to wrap my head around how to create this object/class in a way that let's me use the same functionality of a defaultdict(list) where I can index/append directly AND have it track the global index order of that new value.
dict[key].append(value)
Help would be appreciated - I sunk three hours into trying different solutions before I scrapped it and went with the "just use this method to append" for now.
I have a class where I want to initialize an attribute self.listN and an add_to_listN method for each element of a list, e.g. from attrs = ['list1', 'list2'] I want list1 and list2 to be initialized as empty lists and the methods add_to_list1 and add_to_list2 to be created. Each add_to_listN method should take two parameters, say value and unit, and append a tuple (value, unit) to the corresponding listN.
The class should therefore look like this in the end:
class Foo():
def __init__(self):
self.list1 = []
self.list1 = []
def add_to_list1(value, unit):
self.list1.append((value, unit))
def add_to_list2(value, unit):
self.list2.append((value, unit))
Leaving aside all the checks and the rest of the class, I came up with this:
class Foo():
def __init__(self):
for attr in ['list1', 'list2']:
setattr(self, attr, [])
setattr(self, 'add_to_%s' % attr, self._simple_add(attr))
def _simple_add(self, attr):
def method(value, unit=None):
getattr(self, attr).append((value, unit))
return method
I also checked other solutions such as the ones suggested here and I would like to do it "right", so my questions are:
Are/Should these methods (be) actually classmethods or not?
Is there a cost in creating the methods in __init__, and in this case is there an alternative?
Where is the best place to run the for loop and add these methods? Within the class definition? Out of it?
Is the use of metaclasses recommended in this case?
Update
Although Benjamin Hodgson makes some good points, I'm not asking for a (perhaps better) alternative way to do this but for the best way to use the tools that I mentioned. I'm using a simplified example in order not to focus on the details.
To further clarify my questions: the add_to_listN methods are meant to be additional, not to replace setters/getters (so I still want to be able to do l1 = f.list1 and f.list1 = [] with f = Foo()).
You are making a design error. You could override __getattr__, parse the attribute name, and return a closure which does what you want, but it's strange to dynamically generate methods, and strange code is bad code. There are often situations where you need to do it, but this is not one of them.
Instead of generating n methods which each do the same thing to one of n objects, why not just write one method which is parameterised by n? Something roughly like this:
class Foo:
def __init__(self):
self.lists = [
[],
[]
]
def add(self, row, value):
self.lists[row].append(value)
Then foo.add1(x) becomes simply foo.add(1, x); foo.add2(x) becomes foo.add(2, x), and so on. There's one method, parameterised along the axis of variation, which serves all cases - rather than a litany of ad-hoc generated methods. It's much simpler.
Don't mix up the data in your system with the names of the data in your system.
I asked a similar, yet lousy, question very late last night (Access to instance variable, but not instance method in Python) that caused a fair bit of confusion. I'd delete it if I could, but I can't.
I now can ask my question more clearly.
Background: I'm trying to build a black-jack game to learn python syntax. Each hand is an instance of the Hand class and I'm now at the point where I'm trying to allow for hands to be split. So, when it comes time for a hand to be split, I need to create two new hand instances. Given that further splits are possible, and I want to reuse the same methods for re-splitting hands. I therefore (I think) need to dynamically instantiate the Hand class.
Following is a code snippet I'm using to block out the mechanics:
import os
os.system("clear")
class Hand():
instances=[]
def __init__(self, hand_a, name):
Hand.instances.append(self)
self.name = name
self.hand_a = hand_a
def show_hand(self):
ln = len(self.hand_a)
for x in range(ln):
print self.hand_a[x]
class Creation():
def __init__(self):
pass
def create_name(self):
hil = len(Hand.instances)
new_name = 'hand_' + str(hil + 1)
return(new_name)
def new_instance(self):
new_dict = {0: 'Ace of Clubs', 1: '10 of Diamonds'}
new_hand_name = {}
new_hand_name.setdefault(self.create_name(), None)
print new_hand_name
new_hand_name[0] = Hand(new_dict, self.create_name())
print new_hand_name[0]
hand = Hand("blah", 'hand')
hand_z = Hand("blah_z", 'hand_z')
creation = Creation()
creation.new_instance()
here is the output:
{'hand_3': None}
<__main__.Hand instance at 0x10e0f06c8>
With regard to the instance created by the following statement:
new_hand_name[0] = Hand(new_dict, self.create_name)
Is new_hand_name[0] new the variable that refers to the instance?
Or, is hand_3 the variable?
i.e. when calling an instance method, can I use hand_3.show_hand()?
First, to answer your questions: new_hand_name[0] is the variable that refers to the instance- more specifically, it is the value in the new_hand_name dictionary accessed by the key 0. The new_hand_name dictionary, if you printed it, would look like:
{'hand_3': None, 0: <__main__.Hand instance at 0x10e0f06c8>}
Adding the value of "hand_3" to the dictionary is unnecessary, but for that matter, so is the dictionary.
What you really want to do has nothing to do with dynamic instantiation of new classes, which has nothing to do with your problem. The problem is that a Hand might represent a single list of cards, but might also represent a list of lists of cards, each of which have to be played separately. One great way to solve this is to make a separation between a player and a hand, and allow a player to have multiple hands. Imagine this design (I'm also leaving out a lot of the blackjack functionality, but leaving a little in to give you an idea of how to work this in with the rest of the program).
def draw_random_card():
"""
whatever function returns a new card. Might be in a Deck object, depends on
your design
"""
# some code here
class Player:
def __init__(self):
self.hands = []
def deal(self):
"""add a random hand"""
self.hands.append(Hand([draw_random_card(), draw_random_card()]))
def split(self, hand):
"""split the given hand"""
self.hands.remove(hand)
self.hands += hand.split()
class Hand:
def __init__(self, cards):
self.cards = cards
def hit(self):
"""add a random card"""
self.cards.append(draw_random_card())
def split(self):
"""split and return a pair of Hand objects"""
return [Hand(self.cards[0], draw_random_card()),
Hand(self.cards[1], draw_random_card())]
Isn't that simpler?
In response to your comment:
You can refer to any specific hand as self.hands[0] or self.hands[1] within the Players class.
If you want to keep track of a particular hand, you can just pass the hand itself around instead of passing a character string referring to that hand. Like this:
def process_hand(hand):
"""do something to a hand of cards"""
h.hit()
print h.cards()
h.hit()
h = Hand(cards)
process_hand(h)
This is important: modifications you make to the hand in the function work on the actual hand itself. Why put the extra step of passing a string that you then have to look up?
Also note that information specific to each hand, such as the bet, should probably be stored in the Hand class itself.
If you are sure you want to refer to each hand with a specific name (and again, it's not necessary in this case), you just use a dictionary with those names as keys:
self.hands = {}
self.hands["hand1"] = Hand([card1, card2])
self.hands["hand2"] = Hand([card1, card2])
print self.hands["hand1"]
But again, there is probably no good reason to do this. (And note that this is very different than instantiating a new variable "dynamically". It would be a good idea to look into how dictionaries work).