Python - Print list of objects in a table [duplicate] - python

This question already has answers here:
Printing Lists as Tabular Data
(20 answers)
Closed 9 years ago.
I have a class with several attributes:
class Fighter():
def __init__(self, name = "", gender = "", age = 0, weight = 0, win = 0, loss = 0, ko = 0, date = ""):
self.__name = name
self.__age = age
self.__weight = weight
self.__win = win
self.__loss = loss
self.__ko = ko
self.__date = date
From the user end of my program, an object is created and stored in a list using the following lines in a loop:
s = Fighter(name, gender, age, weight, wins, losses, ko, time)
collection.append(s)
I am able to sort this list by a number of attributes, such as name, weight, etc. and that works perfectly. I also have instance methods inside the class that return the value of a specific attribute, such as:
def fighter_age(self):
return str(self.__age)
My question is, how do I print this information as an organized table, with a column for each attribute, such as:
-----------------------------------------------------------------------
|Fighter Number|Name |Age |Weight |Wins|Losses|Date Added |
|1 |Anderson Silva|39 |185 |33 | 6 |2014-01-17 |

Check the duplicate link, and consider doing:
import sys
def prt_width(str, width):
sys.stdout.write('|' + str + ' '*(width-len(str)) + '|')
sys.stdout.flush()
for fighter in collection:
prt_width(fighter.number)
prt_width(fighter.name)
...
prt_width('\n')
Note: This would require you to calculate the maximun length of each field in advance (for instance, you need to loop through all names and determain the maximum length of a all names before submitting them for print, or just find a standard value that usually works). This is a manual approach, for some reason i tend to argue for going with manual approaches to learn stuff. Check the duplicate link for good libraries that does the thinking for you :)

If you wanted to be a bit clever about this, and use some of the functionality having a class gives you, you could store the width of the widest entry for each instance attribute as a class attribute:
class Fighter():
max_name_len = 4 # start with width of header 'Name'
...
def __init__(self, name, ...):
if len(name) > Fighter.max_name_len :
Fighter.max_name_len = len(name)
...
You can update these class attributes each time you create a new Fighter or change one of the their attributes (see property for how to wrap the attributes that might change with checking code). This saves you from needing to loop through collection each time you want to build the table.
You could then create an instance method to produce the line entry for each instance:
def table_row(self, num): # note argument for fighter number
# return string with appropriate widths
and a class method to do the headers:
#classmethod
def table_header(cls):
# return strings with headers
Then when you print out the table, it looks like:
print Fighter.table_header()
for num, fighter in enumerate(collection, 1):
print fighter.table_row(num)
Here enumerate gives you the number for each Fighter in collection, starting with 1, as well as the actual Fighter objects.

Related

How to get class instance variables, python

I would like to get the names of __init__ parameters and modify them when the code runs. My class looks like this:
class Sample:
def __init__ (self,indicators:dict):
self.names = []
self.returns = 0.0
for k,v in indicators.items():
setattr(self, k, v)
self.names.append(k)
The input of this class is a random choice of items from a lis; then I assign those random items to a dictionary with integer values.
indicatorsList =["SMA", "WMA", "EMA", "STOCHASTIC", "MACD", "HIGHEST_HIGH",
"HIGHEST_LOW", "HIGHEST_CLOSE", "LOWEST_HIGH", "LOWEST_LOW",
"LOWEST_CLOSE", "ATR", "LINGRES", "RSI", "WRSI", "ROC",
"DAY", "MONTH"]
# initializing the value of n
n = random.randint(2,int(math.ceil(len(indicatorsList)/2)))
randomIndList = n * [None]
for i in range(n):
choice = random.choice(indicatorsList)
randomIndList[i] = choice
...
...
sample = Sample(randDict)
Problem is, I don't know the names of these parameters in __init__, and I need to modify them later, for example like this:
sample.sma = random.randint(0, maxVal)
But I don't know if the object will have sma, or ema, or any other attribute, because of the way they're assigned randomly.
First of all, this code:
sample.sma = random.randint(0, maxVal)
will work, even if sample doesn't have an sma attribute. It will create one. Try it yourself and see.
But as you specified in your comment that you only want to modify attributes that already exist, that won't help in this case.
What you could do, with your existing class definition, is to loop over the names attribute you've already defined.
for name in sample.names:
setattr(sample, name, random.randint(0, maxVal))
However, you've basically reinvented a dictionary here, so why not redefine your class to directly use a dictionary?
class Sample:
def __init__(self, indicators:dict):
self.indicators = indicators
Now you no longer need dynamic setattr or getattr lookups. They're simply keys and values:
for key in sample.indicators:
sample.indicators[key] = random.randint(0, maxVal)
(This also means you don't need the separate names attribute.)

How to call up a specific variable in a "database" of defined items

If I am writing a database or something similar, where I define certain items as follows:
def item1():
name = "so and so"
weight = xy
color = "rainbow"
return name,weight,color
If I call the item1() it will return its name weight and color, and if I call name it will call its name.
Also if I make another item, or any number of them, like
def item2():
name = "and so"
weight = z
color = "invisible"
return name, weight, color
I can call their full properties by "itemX()" (the x states for any item, for the sake of example, and is not part of the code)
But how can I now call up only one property of a specific item when there are more items with the same name variable? For example; how can I specifically call up the weight of item2() if needed?
print(weight) gives, naturally, only the first defined variable in the code, so how do I specify that I want that variable of a given specific item? Something like: print(item2(weight)) or something like that. I know that this example doesn't work, but which does?
I believe you need to use classes or named tuples
class Item:
def __init__ (self, name, weight, color):
self.name = name
self.weight = weight
self.color = color
item1 = Item ("so and so", 42, "rainbow")
Then you can access fields via . (dot)
>>> print (item1.color)
"rainbow"

How can I use the value of a variable in the name of another without using a dictionary in python?

The answer people have already given for using the value of a variable in the assignment of another is:
to create a dictionary and,
use dict[oldVariable] instead of defining a new one
I don't think that works in the context of what I'm trying to do...
I'm trying to define a class for a vector which would take a list as an input and assign an entry in the vector for each element of the list.
My code looks something like this right now:
class vector:
def __init__(self, entries):
for dim in range(len(entries)):
for entry in entries:
self.dim = entry #here I want to assign self.1, self.2, etc all the way to however
#many elements are in entries, but I can't replace self.dim with
# dict[dim]
def __str__(self):
string = []
for entry in range(1,4):
string.append(self.entry)
print(string)
How do I do this?
What you are doing here is a bit strange, since you are using a variable named "dim" in a for, but you do not do anything with that variable. It looks like you want to use a class as if it was an array... why don't you define an array within the class and access it from the outside with the index? v.elements[1] ... and so on?
Example:
class Vector:
def __init__(self, entries):
self.elements = []
for e in entries:
self.elements.append(self.process(e))
def __str__(self):
buff = ''
for e in self.elements:
buff += str(e)
return buff
Hope this helps.
If I'm reading your question correctly, I think you're looking for the setattr function (https://docs.python.org/2/library/functions.html#setattr).
If you wanted to name the fields with a particular string value, you could just do this:
class vector:
def __init__(self, entries):
for dim in range(len(entries)):
for entry in entries:
#self.dim = entry
setattr(self, str(dict[dim]), dim)
That will result in your object self having attributes named with whatever the values of dict[dim] are and values equal to the dim.
That being said, be aware that an integer value is generally a poor attribute name. You won't be able to do print obj.1 without error. You'd have to do getattr(obj,'1').
I agree with #Ricardo that you are going about this strangely and you should probably rethink how you're structuring this class, but I wanted to directly answer the question in case others land here looking for how to do dynamic naming.

Can I create object names from a text file in Python 2.7?

I'm working on a game project.
I've created an object, Star(Object).
I want to assign the name of the variables, dynamically, from a text file.
If I have a text file with:
Sol
Centauri
Vega
I want the program to create the Star(Object) with variable names from the text file. I want the process automated, because I'm looking to create hundreds of stars.
I could write the code out by hand:
Sol = Star(Sol)
Centauri = Star(Centauri)
Vega = Star(Vega)
But isn't there a way to automate this?
Essentially, what I eventually want is a tuple with the list of stars, as their own objects. Then, when I am doing game maintenance, I can just iterate over all the objects in the tuple.
The name of a star should not be the name of the variable. Variable names should reflect the context in which the variable is used, e.g. destinationStar or homeStar.
A star's name should be a property of the Star object, accessed via Star.name:
class Star(object):
"""Keeps track of a star."""
def __init__(self, starName):
self.name = starName
# other methods...
def read_stars(filename):
# oversimplified:
stars = {}
starfile = open(filename, "r")
for line in starfile:
words = line.split()
if len(words) == 2 and words[0] == 'star':
name = words[1]
stars[name] = Star(name)
return stars
By storing in a dictionary, you can search for a particular Star with stars[name] or iterate over all the stars with for s in stars.values(), for example.
I want to assign the name of the variables, dynamically This is a very good indication that your design is completely wrong.
It's hard to know exactly what your design is, but I'm going to guess you want to use a dictionary instead.
class BadStar(Exception): pass
class Star(object):
def __init__(self, name, mass, mag, color, x, y, z):
self.name = name
self.mass = float(mass)
self.mag = float(mag)
self.color = color
self.pos = (float(x),float(y),float(z))
#classmethod
def fromstr(cls, s):
"Alternate constructor from string"
stardata = [i.strip() for i in s.split(',')]
if len(stardata)==7:
return cls(*stardata)
else:
raise BadStar("wrong number of arguments in string constructor")
def __str__(self):
x,y,z = self.pos
return "{0} is at ({1}, {2}, {3})".format(self.name, x, y, z)
class StarIndex(dict):
def load(self, fname):
"Load stars from text file"
with open(fname, "r") as f:
for line in f:
line = line.split('#')[0] # discard comments
line = line.strip() # kill excess whitespace
if len(line): # anything left?
try:
star = Star.fromstr(line)
self[star.name] = star
except BadStar:
pass # discard lines that don't parse
return self
and some sample data:
# Name, Mass, Absolute Magnitude, Color, x, y, z
#
# Mass is kg
# Color is rgb hex
# x, y, z are lightyears from earth, with +x to galactic center and +z to galactic north
Sol, 2.0e30, 4.67, 0xff88ee, 0.0, 0.0, 0.0
Alpha Centauri A, 2.2e30, 4.35, 0xfff5f1, -1.676, -1.360, -3.835
then you can load your file like:
s = StarIndex().load("stars.txt")
and
print s["Sol"]
results in
Sol is at (0.0, 0.0, 0.0)
Your question isn't clear. It's clouded by the fact that you're using syntax 'Star(Centauri)', which, in Python, means that you want to create a class called 'Star' that inherits from Centauri. I think what you want is probably a factory object that creates different stars, but then you don't say anything about how the stars might differ. Presumably, the difference is location, but you don't say how that's being handled either.
Best bet, based on guesses, might be to put your star configurations in a YAML file and use pyYAML to load it, which returns a Python data structure ready to go for you.
def makeStar(starName):
globals()[starName] = Star(globals()[starName])
makeStar("Sol")
is the same as
Sol = Star(Sol)
except "Sol" can be replaced with any string (eg the values read in from that file).
Also, you may want to rethink making these global variables - it prevents you from being able to iterate through all the stars, should you need to, and could possibly cause naming conflicts. If you want these to be in a dictionary, then just replace "globals()" with the name of your dictionary.
You probably should use a dictionary for that. It is possible to create dinamic variable names, but it would make no sense, since to access then you would need an indirect reference anyway.
stars = {}
with open("stars.txt") as stars_file:
for star_name in stars_file:
star_name = star_name.strip()
stars[star_name] = Star(star_name)
You can use the types module to create class objects at the run time:
import types
def make_class(name):
cls = types.ClassType(name, (), {})
return cls
cls = make_class("Star")
obj = cls()
In the above example, cls becomes your class Star

How can I define a class in Python?

Quite simple, I'm learning Python, and I can't find a reference that tells me how to write the following:
public class Team {
private String name;
private String logo;
private int members;
public Team(){}
// Getters/setters
}
Later:
Team team = new Team();
team.setName("Oscar");
team.setLogo("http://....");
team.setMembers(10);
That is a class Team with the properties: name/logo/members
Edit
After a few attempts I got this:
class Team:
pass
Later
team = Team()
team.name = "Oscar"
team.logo = "http://..."
team.members = 10
Is this the Python way? It feels odd (coming from a strongly typed language of course).
Here is what I would recommend:
class Team(object):
def __init__(self, name=None, logo=None, members=0):
self.name = name
self.logo = logo
self.members = members
team = Team("Oscar", "http://...", 10)
team2 = Team()
team2.name = "Fred"
team3 = Team(name="Joe", members=10)
Some notes on this:
I declared that Team inherits from object. This makes Team a "new-style class"; this has been recommended practice in Python since it was introduced in Python 2.2. (In Python 3.0 and above, classes are always "new-style" even if you leave out the (object) notation; but having that notation does no harm and makes the inheritance explicit.) Here's a Stack Overflow discussion of new-style classes.
It's not required, but I made the initializer take optional arguments so that you can initialize the instance on one line, as I did with team and team3. These arguments are named, so you can either provide values as positional parameters (as with team) or you can use the argument= form as I did with team3. When you explicitly specify the name of the arguments, you can specify arguments in any order.
If you needed to have getter and setter functions, perhaps to check something, in Python you can declare special method functions. This is what Martin v. Löwis meant when he said "property descriptors". In Python, it is generally considered good practice to simply assign to member variables, and simply reference them to fetch them, because you can always add in the property descriptors later should you need them. (And if you never need them, then your code is less cluttered and took you less time to write. Bonus!)
Here's a good link about property descriptors: http://adam.gomaa.us/blog/2008/aug/11/the-python-property-builtin/
Note: Adam Gomaa's blog seems to have disappeared from the web. Here's a link to a saved copy at archive.org:
https://web.archive.org/web/20160407103752/http://adam.gomaa.us/blog/2008/aug/11/the-python-property-builtin/
It doesn't really matter if you specify values as part of the call to Team() or if you poke them into your class instance later. The final class instance you end up with will be identical.
team = Team("Joe", "http://example.com", 1)
team2 = Team()
team2.name = "Joe"
team2.logo = "http://example.com"
team2.members = 1
print(team.__dict__ == team2.__dict__)
The above will print True. (You can easily overload the == operator for Team instances, and make Python do the right thing when you say team == team2, but this doesn't happen by default.)
I left out one thing in the above answer. If you do the optional argument thing on the __init__() function, you need to be careful if you want to provide a "mutable" as an optional argument.
Integers and strings are "immutable". You can never change them in place; what happens instead is Python creates a new object and replaces the one you had before.
Lists and dictionaries are "mutable". You can keep the same object around forever, adding to it and deleting from it.
x = 3 # The name "x" is bound to an integer object with value 3
x += 1 # The name "x" is rebound to a different integer object with value 4
x = [] # The name "x" is bound to an empty list object
x.append(1) # The 1 is appended to the same list x already had
The key thing you need to know: optional arguments are evaluated only once, when the function is compiled. So if you pass a mutable as an optional argument in the __init__() for your class, then each instance of your class shares one mutable object. This is almost never what you want.
class K(object):
def __init__(self, lst=[]):
self.lst = lst
k0 = K()
k1 = K()
k0.lst.append(1)
print(k0.lst) # prints "[1]"
print(k1.lst) # also prints "[1]"
k1.lst.append(2)
print(k0.lst) # prints "[1, 2]"
The solution is very simple:
class K(object):
def __init__(self, lst=None):
if lst is None:
self.lst = [] # Bind lst with a new, empty list
else:
self.lst = lst # Bind lst with the provided list
k0 = K()
k1 = K()
k0.lst.append(1)
print(k0.lst) # prints "[1]"
print(k1.lst) # prints "[]"
This business of using a default argument value of None, then testing that the argument passed is None, qualifies as a Python design pattern, or at least an idiom you should master.
class Team:
def __init__(self):
self.name = None
self.logo = None
self.members = 0
In Python, you typically don't write getters and setters, unless you really have a non-trivial implementation for them (at which point you use property descriptors).
To write classes you would normally do:
class Person:
def __init__(self, name, age, height):
self.name = name
self.age = age
self.height = height
To instantiate instances of a class(es) you would do
person1 = Person("Oscar", 40, "6ft")
person2 = Team("Danny", 12, "5.2ft")
You can also set a default value:
class Person:
def __init__(self):
self.name = "Daphne"
self.age = 20
self.height = "5.4ft"
To instantiate a classes set like this, you want to do:
person3 = Person()
person3.name = "Joe"
person3.age = 23
person3.height = "5.11ft"
You will notice that this method bears a lot of similarity to your typical Python dictionary interaction.

Categories

Resources