Python - Nested Classes (Parent-Child [New to Classes]) - python

I have this data
{Wednesday : {22 : {Type = x,
Temp = x,
Speed = x,
Direction = x}
{23 : {Type = x,
Temp = x,
Speed = x,
Direction = x}
I am trying to write a class so that I will be able to access it by calling as an example and that will give me X.
My code so far is this :
class Weather(object):
def __init__(self, wtype, wtemp, wspeed, wdirection):
self.type = wtype
self.temp = wtemp
self.speed = wspeed
self.direction = wdirection
This allows me to get the data when calling upon the date :
Wednesday.Temp
>>> 22
However I also need to allocate the data by time as well as the date, so when calling "Wednesday.22.Type" I get the specific date for that our.
I am new to classes within Python and I'm not quite sure on how to build the class so that I can call on the date, and then the time to get the respective data. I'm assuming that a nested class is needed to have a "parent-child" like relation in the code however I'm not sure how to do this.

Although numbers are not considered to be valid identifiers in Python (but that could be funny for trolling: 0 = 1 = 2 = 3 = 42), things like _3 are, but are generally considered to be "private" attributes by the python community (myself included), so i use at followed by the number instead. And I think it would be better to access it like you access a dictionary.
Here is my take on it. Remove the methods if you don't want the associated feature.
class SpecificWeather(object):
def __init__(self, data):
self.data = data
#property
def type(self):
return self.data["Type"]
#property
def temperature(self):
return self.data["Temp"]
#property
def speed(self):
return self.data["Speed"]
#property
def direction(self):
return self.data["Direction"]
class Weather(object):
def __init__(self, data): # data is the dictionary
self.data = data
def __getitem___(self, item): # for wednesday[22].type
return SpecificWeather(self.data[item])
def __getattr__(self, name): # for wednesday.at22.type
if name.startswith("at"):
return SpecificWeather(self.data[int(name[2:])])
raise AttributeError()
#property
def type(self):
# TODO: Return the average type or something like that
#property
def temperature(self):
# TODO: Return the average temperature or something like that
#property
def speed(self):
# TODO: Return the average speed or something like that
#property
def direction(self):
# TODO: Return the average direction or something like that
This solution uses property a lot, this has a big advantage: If you change the temperate for 22, wednesday[22].temperature will now give you the new value. If you care for performance however, and you use only half of them, then this one can be faster than storing the results, if you access them even multiple times though, this will be a lot slower.
How to use it:
wednesday = Weather({
22: {
'Type': ...,
'Temp': 30,
'Speed': ...,
'Direction': ...
},
23: {
'Type': ...,
'Temp': 28,
'Speed': ...,
'Direction': ...
}
})
print(wednesday.at22.temperature) # gives 30
print(wednesday[23].temperature) # gives 28

Related

Define a function to update a class integer property

I'm having issues defining a function that controls the correct "speed" (accelerate/slow down).
I currently have my code like this:
class Train():
serial_nummer = 2021001
def __init__(self, type, buildyear):
self.type = type
self.buildyear = buildyear
self.serial_nummer = Train.serial_nummer
self.speed = ()
Train.serial_nummer += 1
return
def snelheid(self, speed):
pass
def __str__(self):
return (f"Train type: {self.type} \nBuildyear: {self.buildyear} \nSerial number: {self.serial_nummer} \nCurrent speed: {self.speed}\n\n")
train1 = Train('Alfatrain', '2001')
train1.speed = 100
print (train1)
How can I create a function that controls the correct "speed"?
Now I'm just modifying the speed with train.speed = 100.
You don't really need a function when you can use train.speed += amount. You will want to initialize the speed as 0, not an empty tuple, though
Without more clarity, I'm guessing instructions are looking for
def accelerate(self, amount):
self.speed += amount
def decelerate(self, amount):
self.accelerate(-1*amount)

How to get the number of input arguments of class(__init__)?

I want to make the class score in python that takes each subject's score and return its average and sum. I know if I receive 4 arguments than denominator of average should be 4, but how I can make it to fixed codes that changed by number of inputs?, not just count numbers and type 4. I tried Len(self) or using for loop to count but Len makes error and for loop isn't that easy.
class score:
def __init__(self, language, math, english, science):
self.language = language
self.math = math
self.english = english
self.science = science
...
def sum_scores(self):
result = self.language + self.math + self.english + self.science
return result
def average(self):
average = self.sum_scores()/4 # Here is the problem!
return average
This is my first question on stack. so sorry for my poor English and stupid questions.
Don't use 4 separate attributes in the first place. Use a dict to store the attributes; then you can query the size of the dict.
class score:
def __init__(self, language, math, english, science):
self.scores = {'language': language, 'math': math, 'english': english, 'science': science}
...
def sum_scores(self):
return sum(self.scores.values())
def average(self):
return self.sum_scores() / len(self.scores)
If you still want attributes for each individual score, use properties:
class score:
def __init__(self, language, math, english, science):
self.scores = {'language': language, 'math': math, 'english': english, 'science': science}
#property
def language(self):
return self.scores['language']
# etc.
def sum_scores(self):
return sum(self.scores.values())
def average(self):
return self.sum_scores() / len(self.scores)
Assuming that it is OK that you don't know the subjects for which the scores apply, you can just use unnamed positional arguments
class score:
def __init__(self, *args):
self.scores = args
...
def sum_scores(self):
return sum(self.scores)
def average(self):
return self.sum_scores() / len(self.scores)
For your case, you could just note that the quantity is 4
However, you may want to use **kwargs as an argument (you can use any name, this is just convention; ** assigns all the keyword args not specifically named to a dict) instead
SUBJECTS = ("math", "science" ... )
class MyClass():
def __init__(self, **kwargs): # also consider *args
self.subjects = {k: v for k, v in kwargs.items() if k in SUBJECTS}
def sum_scores(self):
return sum(self.subjects.values())
def average(self):
return self.sum_scores() / len(self.subjects)
>>> c = MyClass(math=10, science=8)
>>> c.sum_scores()
18
>>> c.average()
9.0
You may also want to consider reporting on arguments that are not used to help users find usage errors
You can use keyword arguments in your init function:
class Score:
def __init__(self, **scores):
self.classes = []
for _class, score in scores.items():
setattr(self, _class, score) # self.english, self.math, etc.
self.classes.append(_class) # ["math", "english"]
def sum_scores(self):
sum = 0
for i in self.classes:
sum += getattr(self, i)
return sum
def average(self):
return self.sum_scores() / len(self.classes)
By using **scores, you can dynamically iterate over all arguments passed into the function as a dictionary. Here, I use the setattr function to give the Score object a property of its class name, and its value is the corresponding value from the dictionary. That way, I can dynamically iterate over each class, whether 3 or 100 classes are provided as input.
score1 = Score(math=67, english=98, art=76)
print(score1.sum_scores())
print(score1.average())
>>> 241
>>> 80.3333333333

Python: Construct class (and variable names) through a function?

I recently started to work with Python's classes, since I need to work with it through the use of OTree, a Python framework used for online experiment.
In one file, I define the pages that I want to be created, using classes. So essentially, in the OTree system, each class corresponds to a new page. The thing is, all pages (so classes) are basically the same, at the exception to some two parameters, as shown in the following code:
class Task1(Page):
form_model = 'player'
form_fields = ['Envie_WordsList_Toy']
def is_displayed(self):
return self.round_number == self.participant.vars['task_rounds'][1]
def vars_for_template(player):
WordsList_Toy= Constants.WordsList_Toy.copy()
random.shuffle(WordsList_Toy)
return dict(
WordsList_Toy=WordsList_Toy
)
#staticmethod
def live_method(player, data):
player.WTP_WordsList_Toy = int(data)
def before_next_page(self):
self.participant.vars['Envie_WordsList_Toy'] = self.player.Envie_WordsList_Toy
self.participant.vars['WTP_WordsList_Toy'] = self.player.WTP_WordsList_Toy
So here, the only thing that would change would be the name of the class, as well as the suffix of the variable WordsList_ used throughout this code, which is Toy.
Naively, what I tried to do is to define a function that would take those two parameters, such as this:
def page_creation(Task_Number,name_type):
class Task+str(Task_Number)(Page):
form_model = 'player'
form_fields = ['Envie_WordsList_'+str(name_type)]
def is_displayed(self):
return self.round_number == self.participant.vars['task_rounds'][1]
def vars_for_template(player):
WordsList_+str(name_type) = Constants.WordsList+str(name_type).copy()
random.shuffle(WordsList_+str(name_type))
return dict(
WordsList_+str(name_type)=WordsList_+str(name_type)
)
#staticmethod
def live_method(player, data):
player.WTP_WordsList_+str(name_type) = int(data)
def before_next_page(self):
self.participant.vars['Envie_WordsList_+str(name_type)'] = self.player.Envie_WordsList_+str(name_type)
self.participant.vars['WTP_WordsList_+str(name_type)'] = self.player.WTP_WordsList_+str(name_type)
Obviously, it does not work since I have the feeling that it is not possible to construct variables (or classes identifier) this way. I just started to really work on Python some weeks ago, so some of its aspects might escape me still. Could you help me on this issue? Thank you.
You can generate dynamic classes using the type constructor:
MyClass = type("MyClass", (BaseClass1, BaseClass2), {"attr1": "value1", ...})
Thus, according to your case, that would be:
cls = type(f"Task{TaskNumber}", (Page, ), {"form_fields": [f"Envive_WordList_{name_type}"], ...})
Note that you still have to construct your common methods like __init__, is_displayed and so on, as inner functions of the class factory:
def class_factory(*args, **kwargs):
...
def is_displayed(self):
return self.round_number == self.participant.vars['task_rounds']
def vars_for_template(player):
...
# Classmethod wrapping is done below
def live_method(player, data):
...
cls = type(..., {
"is_displayed": is_displayed,
"vars_for_template": vars_for_template,
"live_method": classmethod(live_method),
...,
}
#classmethod could be used as a function - {"live_method": classmethod(my_method)}

Property set correctly inside an object but not accessible outside

I'm working under python pyramid, with Python3.
I have a model that looks like this:
class OneTimeCode(Base):
__tablename__ = 'otc_one_time_codes'
otc_one_time_code_id = Column(Integer, primary_key=True)
otc_one_time_code = Column(String(32))
otc_usr_user_id = Column(Integer, ForeignKey('usr_users.usr_user_id'), nullable=True)
otc_expire_time = Column(DateTime)
def __init__(self, otc_usr_user_id, otc_expire_time=None):
self.otc_usr_user_id = otc_usr_user_id
if otc_expire_time is None:
self.otc_expire_time = (datetime.now() + timedelta(6*365/12)).isoformat()
else:
self.otc_expire_time = otc_expire_time
#classmethod
def get_code(self, hlength=6):
seed = datetime.now() + timedelta(random.randrange(1,10000))
tmp_hash = hashlib.md5(seed.strftime("%Y-%m-%d %H:%M:%S.%F").encode('utf-8')).hexdigest()
if hlength == 32:
self.otc_one_time_code = tmp_hash
else:
self.otc_one_time_code = tmp_hash[0 : hlength]
print(self.otc_one_time_code)
The problem is, when I instantiate one of these objects and then explicitly call get_code, the print line at the end prints to the screen the code successfully.
However, in my view, if I explicitly try to print that property, it's 'None'
Here's what my view code looks like:
otc = OneTimeCode(
otc_usr_user_id = user.usr_user_id
)
otc.get_code()
pprint.pprint(vars(otc))
session.add(otc)
And the console output looks like this:
0d097c
{'_sa_instance_state': <sqlalchemy.orm.state.InstanceState object at 0x50877d0>, 'otc_expire_time': '2015-02-13T10:56:14.244447', 'otc_usr_user_id': 1} 2014-08-14 22:56:14,245
INFO [sqlalchemy.engine.base.Engine][Dummy-2] INSERT INTO otc_one_time_codes (otc_one_time_code, otc_usr_user_id, otc_expire_time) VALUES (%(otc_one_time_code)s, %(otc_usr_user_id)s, %(otc_expire_time)s) RETURNING otc_one_time_codes.otc_one_time_code_id 2014-08-14 22:56:14,245
INFO [sqlalchemy.engine.base.Engine][Dummy-2] {'otc_one_time_code': None, 'otc_expire_time': '2015-02-13T10:56:14.244447', 'otc_usr_user_id': 1} 2014-08-14 22:56:14,247
INFO [sqlalchemy.engine.base.Engine][Dummy-2] COMMIT
You can see the value inside the model: 0d097c, and also the pprint object, where it doesn't look like the property exists.
Why can't I get access to this property?
Looks like you should be using a #property instead of a OTC, however it also seems like this may be something you DON'T want to calculate each time!
# for all the docstrings, let multi = Multi(2)
class Multi(object):
def __init__(self, attribute):
"""When instantiated, set self.attribute to attribute"""
self.attribute = attribute
#property
def attribute_times_ten(self):
"""accessed via multi.attribute_times_ten
and will return 20. Use properties to signify
a variable that requires some work done to it
that needs to calculated each time it's called."""
return attribute_times_ten
#classmethod
def times_ten(cls, num):
"""Not the best example, but a #classmethod will
give the class as its first argument, NOT the
instance. This is useful in lots of constructor
settings, e.g. CreateClass.fromstring("attributes")"""
return num * 5
def generate_number(self, multiplier):
"""This is just a normal method. This is what I think
you want, tbh, and you should probably call it in your
__init__ method since you NEED this to run in your OTC
for it to work as intended. Methods (like properties)
are automagically passed the instance as the first
argument, so we can CHANGE self.attribute with that."""
self.attribute = self.attribute * multiplier
Docstrings should be self descriptive, but:
multi = Multi(2)
multi.attribute_times_ten # returns 20
Multi.times_ten(8) # returns 80, note the capital M!
multi.generate_number(3) # self.attribute is now 6
multi.attribute_times_ten # returns 60
A real-world case where you might need all of the above:
class _Tile(object):
def __init__(self, x, y):
"""A naive implementation of Tile that doesn't care
what its side length is and doesn't have any properties
to hide its attributes"""
self.x = x
self.y = y
#classmethod
def tiles_to_pixels(cls, tile):
return cls(tile._x * tile.side_length, tile._y * tile.side_length)
#classmethod
def tiles_to_tiles(cls, tile):
return cls(tile._x, tile._y)
class Tile(object):
def __init__(self, x, y, side_length):
"""A tile object in a map"""
self._x = x # x-coord in tiles
self._y = y # y-coord in tiles
self.side_length = side_length # pixels per tile
#property
def in_pixels(self):
"""self.in_pixels returns an object whose .x and .y
correspond to the x and y position IN PIXELS of the
top-left corner of the tile."""
_tile = _Tile.tiles_to_pixels(self)
return _tile
#property
def in_tiles(self):
"""self.in_tiles returns an object whose .x and .y
correspond to the x and y position IN TILES of the
top-left corner of the tile."""
_tile = _Tile.tiles_to_tiles(self)
return _tile
def change_side_length(self, new_length):
"""Use to change the side length. This can break
your whole map since it's naive, so be careful."""
self.side_length = new_length
my_tile = Tile(0,0,32) # 32 pixel tile starting at (0,0)
my_tile.x # NameError, since it's called my_tile._x
my_tile.in_tiles.x # 0
my_tile.in_pixels.y # 0
other_tile = Tile(4,7,32) # 32 pixel tile starting at (4,7)
other_tile.y # NameError, see above
other_tile.in_tiles.y # 7
other_tile.in_pixels.x # 128

Python: referencing class object list of lists

I am fairly new to python. I have tried to define a class, I then want to create an instance from a file, then refer to specific pieces of it, but cannot seem to. This is Python 3.3.0
Here's the class....
class Teams():
def __init__(self, ID = None, Team = None, R = None, W = None, L = None):
self._items = [ [] for i in range(5) ]
self.Count = 0
def addTeam(self, ID, Team, R=None, W = 0, L = 0):
self._items[0].append(ID)
self._items[1].append(Team)
self._items[2].append(R)
self._items[3].append(W)
self._items[4].append(L)
self.Count += 1
def addTeamsFromFile(self, filename):
inputFile = open(filename, 'r')
for line in inputFile:
words = line.split(',')
self.addTeam(words[0], words[1], words[2], words[3], words[4])
def __len__(self):
return self.Count
Here's the code in Main
startFileName = 'file_test.txt'
filename = startFileName
###########
myTestData = Teams()
myTestData.addTeamsFromFile(startFileName)
sample data in file
100,AAAA,106,5,0
200,BBBB,88,3,2
300,CCCC,45,1,4
400,DDDD,67,3,2
500,EEEE,90,4,1
I think I am good to here (not 100% sure), but now how do I reference this data to see... am i not creating the class correctly? How do I see if one instance is larger than another...
ie, myTestData[2][2] > myTestData[3][2] <----- this is where I get confused, as this doesn't work
Why don't you create a Team class like this :
class Team():
def __init__(self, ID, Team, R=None, W = 0, L = 0)
# set up fields here
Then in Teams
class Teams():
def __init__(self):
self._teams = []
def addTeam (self, ID, Team, R=None, W = 0, L = 0)
team = Team (ID, Team, R=None, W = 0, L = 0)
self._teams.append (team)
Now If i got it right you want to overwrite the > operator's behaviour.
To do that overload __gt__(self, other) [link]
So it will be
class Team ():
# init code from above for Team
def __gt__ (self, otherTeam):
return self.ID > otherTeam.ID # for example
Also be sure to convert those strings to numbers because you compare strings not numbers. Use int function for that.
The immediate problem you're running into is that your code to access the team data doesn't account for your myTestData value being an object rather than a list. You can fix it by doing:
myTestData._items[2][2] > myTestData._items[3][2]
Though, if you plan on doing that much, I'd suggest renaming _items to something that's obviously supposed to be public. You might also want to make the addTeamsFromFile method convert some of the values it reads to integers (rather than leaving them as strings) before passing them to the addTeam method.
An alternative would be to make your Teams class support direct member access. You can do that by creating a method named __getitem__ (and __setitem__ if you want to be able to assign values directly). Something like:
def __getitem__(self, index):
return self._items[index]
#Aleksandar's answer about making a class for the team data items is also a good one. In fact, it might be more useful to have a class for the individual teams than it is to have a class containing several. You could replace the Teams class with a list of Team instances. It depends on what you're going to be doing with it I guess.

Categories