Using a class of variables for function argument - python

I am new to python, about 3 days, and I am not sure if I have worded the question properly.
I have a class:
class blue_slime:
nom = "BLUE SLIME"
img = " /\ \n( o o)"
str = 10
int = 5
dex = 5
con = 10
spd = 10
hp = ((con + str) / 2)
mp_bonus = 1
and I want to use the variables from this class in another function.
def encounter(nom, hp, img):
print(char_name + " encountered a " + nom + "!!!")
wait()
while hp > 0:
battle(hp, img)
else:
stats()
now I know that I could call this by using
encounter(blue_slime.nom, blue_slime.hp, blue_slime.img)
but I would much rather (and think it may be necessary for my program down the line) be able to just use the class name as the function argument and then in the function I can just utilize all variables without having to write them in each time. While this may sound like laziness, I am thinking about making the encounter be random, so 10% chance to encounter(blue_slime) 10% chance to encounter(green_slime).
I feel the easiest way to implement this would be to somehow condense all the variables in "class blue_slime" into one name.
please let me know if there is a way to do this, perhaps I have just not learned it yet.

You can just pass the class into the function is that's what you wanna do. That will solve your problem:
def encounter(monster):
monster.hp
monster.img
# etc.
Here's some tips:
As was already mentioned in the comments on your question, you probably want to be using instances of those classes instead of the actual classes. I'll give a sample class with a few pointers:
class BlueSlime: # Using CapCase like this is normal for class names
# You can use this space to make class variables.
# These will be the same across all the classes.
# Probably use this for initializing your instances
base_hp = 5
base_int = 10
base_dmg = 3
def __init__(self): # The "Constructor" of your instances
self.current_hp = self.base_hp # self refers to the instances
self.int = self.base_int
self.dmg = self.base_dmg
The instance thing is nice because if some of your slimes take dmg, you don't necessarily want them all to take dmg.
bs1 = BlueSlime() # Init the instance
bs2 = BlueSlime()
# bs1 takes 1 dmg (May want a method to do this)
bs1.hp -= 1
bs1.hp
# Now 4
bs2.hp
# Still 5
Getting back to your question, at this point, you can pass these instances into your encounter function.
def encounter(monster):
# Something happens here to reduce the hp, it will reduce the instance's hp
# Something happens here to deal dmg, you can look it up on the instance
player.hp -= monster.dmg # Something like this....
# etc

Related

Controlling dynamic properties in Shady according to video frames not time

I am trying to use Shady to present a sequence of image frames. I'm controlling the flow from another machine, so that I first instruct the machine running Shady to present the first frame, and later on to run the rest of the frames.
I create a World instance, and attach to it an animation callback function. Within this callback I listen for communications from the other machine (using UDP).
First I receive a command to load a given sequence (stored as a numpy array), and I do
def loadSequence(self, fname):
yy = np.load(fname)
pages = []
sz = yy.shape[0]
for j in range(yy.shape[1]/yy.shape[0]):
pages.append(yy[:, j*sz:(j+1)*sz])
deltax, deltay = (self.screen_px[0] - sz) / 2, (self.screen_px[1] - sz) / 2
if (self.sequence is None):
self.sequence = self.wind.Stimulus(pages, 'sequence', multipage=True, anchor=Shady.LOCATION.UPPER_LEFT, position=[deltax, deltay], visible=False)
else:
self.sequence.LoadPages(pages, visible=False)
When I receive the command to show the first frame, I then do:
def showFirstFrame(self, pars):
self.sequence.page = 0 if (pars[0] == 0) else (len(self.sequence.pages) - 1)
self.sequence.visible = True
But what do I do now to get the other frames to be be displayed? In the examples I see, s.page is set as a function of time, but I need to show all frames, regardless of time. So I was thinking of doing something along these lines:
def showOtherFrames(self, pars, ackClient):
direction, ack = pars[0], pars[2]
self.sequence.page = range(1, len(self.sequence.pages)) if (direction == 0) else range(len(self.sequence.pages)-2, -1, -1)
But this won't work. Alternatively I thought of defining a function that takes t as argument, but ignores it and uses instead a counter kept in a global variable, but I'd like to understand what is the proper way of doing this.
When you make s.page a dynamic property, the function assigned to it must take one argument (t), but you can still just use any variables in the space when defining that function, and not even use the time argument at all.
So, for example, you could do something as simple as:
w = Shady.World(...)
s = w.Stimulus(...)
s.page = lambda t: w.framesCompleted
which will set the page property to the current frame count. That sounds like it could be useful for your problem.
Your global-variable idea is one perfectly valid way to do this. Or, since it looks like you're defining things as methods of an instance of your own custom class, you could use instance methods as your animation callbacks and/or dynamic property values—then, instead of truly global variables, it makes sense to use attributes of self:
import Shady
class Foo(object):
def __init__(self, stimSources):
self.wind = Shady.World()
self.stim = self.wind.Stimulus(stimSources, multipage=True)
self.stim.page = self.determinePage # dynamic property assignment
def determinePage(self, t):
# Your logic here.
# Ignore `t` if you think that's appropriate.
# Use `self.wind.framesCompleted` if it's helpful.
# And/or use custom attributes of `self` if that's
# helpful (or, similarly, global variables if you must).
# But since this is called once per frame (whenever the
# frame happens to be) it could be as simple as:
return self.stim.page + 1
# ...which is indefinitely sustainable since page lookup
# will wrap around to the number of available pages.
# Let's demo this idea:
foo = Foo(Shady.PackagePath('examples/media/alien1/*.png'))
Shady.AutoFinish(foo.wind)
Equivalent to that simple example, you could have the statement self.stim.page += 1 (and whatever other logic) inside a more-general animation callback.
Another useful tool for frame-by-frame animation is support for python's generator functions, i.e. functions that include a yield statement. Worked examples are included in python -m Shady demo precision and python -m Shady demo dithering.
It can also be done in a StateMachine which is always my preferred answer to such things:
import Shady
class Foo(object):
def __init__(self, stimSources):
self.wind = Shady.World()
self.stim = self.wind.Stimulus(stimSources, multipage=True)
foo = Foo(Shady.PackagePath('examples/media/alien1/*.png'))
sm = Shady.StateMachine()
#sm.AddState
class PresentTenFrames(sm.State):
def ongoing(self): # called on every frame while the state is active
foo.stim.page += 1
if foo.stim.page > 9:
self.ChangeState()
#sm.AddState
class SelfDestruct(sm.State):
onset = foo.wind.Close
foo.wind.SetAnimationCallback(sm)
Shady.AutoFinish(foo.wind)

Give a number units when used through a class

I'm trying to get a class to spit out a number with units. The purpose of it having units would be to be able to work with the number later, but without the units getting in the way as a string. Though I have no idea how to do this. I'm quite new with (actually understanding) classes, I'm not even sure if what I ask is possible or not.
My goal is to make a class for making atoms, based on their protons, electrons and neutons. The thing I wanna try is to be able to see the units of the mass (in kgs for my purpose) when I try printing it. Here's what I have:
class Atom:
def __init__(self, protons, electrons, neutrons):
ElemCharge = 1.6021765*(10**-19)
UMAS = 1.66054*(10**-27)
self.protons = protons
self.electrons = electrons
self.neutrons = neutrons
self.charge = ElemCharge*(self.protons - self.electrons)
self.mass = (self.protons + self.neutrons)*UMAS
def main():
Hydrogen = Atom(1,1,0)
print (Hydrogen.mass)
Argon = Atom(18,18,22)
print (Argon.mass)
if __name__ == "__main__":
main()
This code works completely fine, so no worries about that :p
Is there a way to even do this? If so, how can it be done? Thanks!
I'm not sure if this is exactly what you're looking for, but you could have a method inside the Atom class that prints the mass of the atom plus a unit string.
class Atom:
def __init__(self, protons, electrons, neutrons):
ElemCharge = 1.6021765*(10**-19)
UMAS = 1.66054*(10**-27)
self.protons = protons
self.electrons = electrons
self.neutrons = neutrons
self.charge = ElemCharge*(self.protons - self.electrons)
self.mass = (self.protons + self.neutrons)*UMAS
def print_mass_str(self):
print('{} {}'.format(self.mass, 'kg'))
Then this:
Argon = Atom(18,18,22)
print(Argon.mass)
Argon.print_mass_str()
would print:
6.64216e-26
6.64216e-26 kg
There are ways of doing it, but firstly you should ask yourself whether you want this. Having different units can get confusing. You can put conversions at the appropriate places (e.g. after reading the input from user) and have your system work solely on a given unit (that's why we have SI units, after all). Being able to support multiple units internally not only complicates the code, but introduces another source of possible confusion.
There are packages which do this for you, such as units, numericalunits or pint.
An example taken from units documentation is:
>>> from units import unit
>>> metre = unit('m')
>>> second = unit('s')
>>> print(metre(10) / second(2))
5.00 m / s
>>> print(metre(10) ** 3)
1000.00 m * m * m
See how metre creates a meter value, and it keeps track of its usage. It has also support for defining custom units.
Therefore, you could simply store the values in your class as values from units or other package, and you're all set. I looked a bit into the code of units and is quite short and I think it's a good source of learning how to handle stuff like this in your own code.
I have to cite the failure of Nasa Mars Climate Orbiter, which was due to a unit discrepancy.
To override the string representation of the number, make a wrapper class:
class FloatWrapper:
def __init__(self, float, unit):
self.float = float
self.unit = unit
def __getattr__(self, key):
try:
return object.__getattr__(self, key)
except AttributeError:
return getattr(self.float, key)
def __str__(self):
return str(self.float) + self.unit
Now in your __init__, after defining self.mass, add:
self.mass = FloatWrapper(self.mass, 'kg')
This works?
Basically I have added another property to the Atom class so that all objects of type Atom can use it. Another way of doing it would be to pass it as you are doing with the neutrons/protons/electrons. That way it you can perform your own calculations (if there was a requirement to do so) given a unit (say you wanted to show weight in grams for Hydrogen , while in KGs for Argon)
class Atom:
def __init__(self, protons, electrons, neutrons):
ElemCharge = 1.6021765*(10**-19)
UMAS = 1.66054*(10**-27)
self.protons = protons
self.electrons = electrons
self.neutrons = neutrons
self.charge = ElemCharge*(self.protons - self.electrons)
self.mass = (self.protons + self.neutrons)*UMAS
self.massUnit = "kgs"
def main():
Hydrogen = Atom(1,1,0)
print ("{0} {1}".format(Hydrogen.mass,Hydrogen.massUnit))
Argon = Atom(18,18,22)
print ("{0} {1}".format(Argon.mass,Argon.massUnit))
if __name__ == "__main__":
main()
Output
1.66054e-27 kgs
6.64216e-26 kgs

Function runs by itself but not when in a class

class Fibonacci:
def fn(num):
if num <= 0:
return 0
if num <= 1:
return 1
else:
Result = fn(num - 1) +fn(num - 2)
return Result
amount = int(input("How many numbers do you want? : "))
i = 1
while i < amount:
FibValue = fn(i)
print(FibValue)
i += 1
Fibonacci()
Here are my errors:
File "C:/Users/Carsten/PycharmProjects/untitled/Lesson13.py", line 30, in <module>
class Fibonacci:
File "C:/Users/Carsten/PycharmProjects/untitled/Lesson13.py", line 45, in Fibonacci
FibValue = fn(i)
File "C:/Users/Carsten/PycharmProjects/untitled/Lesson13.py", line 38, in fn
Result = fn(num - 1) +fn(num - 2)
NameError: name 'fn' is not defined
I'm not quite sure as to why I can run the def Fibonacci: function on its own but as soon as I put it under a class it gives me these errors. I'm still a beginner and have little idea of what these errors mean but even looking them up isn't of much assistance. Any help is appreciated. I realize I can use this as just a standalone function but I'm in the middle of a problem in the youtube series I'm watching to teach myself and I don't want to simply skip ahead in the video and just see the answer to the rest of the problem. Thanks
When you change the function to a class method (i.e. put it inside the class), then only an object of that class can invoke the function. The syntax is as stanleyli mentioned. Perhaps a little more clear:
fib_obj = Fibonacci()
fib_value = fib_obj.fn(i)
As Ignazio already pointed out, this is a very poor example of Class usage. The class has no attributes (data associated with the object), and making the class only makes it harder to use the function.
A Class is better used when you have a collection of data and functions that act on that data, where the combination embodies a higher-level concept. For instance, a game board with piece locations and movement functions would be a good use of a class.
I understand now. Also see the tutorial references here. Essentially, an iterable is a Class that includes a next (explicit) and/or __next__ (implicit) method. That's why you start with the class structure.
Once you've done that, your main program reduces to
for i in Fibonacci(amount):
print i

Adding two __init__ variables not calculating correctly (should be simple solve, I'm noob to classes)

I'm making a simple text-based game.
My full code is 150 lines, so I'll include what I think is relevant. Let me know if you need more.
The problem is that this line:
print("You deal " + str(hero.damage) + " damage to the monster")
returns only 5 instead of 5 + level as wanted.
class Hero:
def __init__(self):
self.level = 0
self.damage = 5 + self.level #This is the problem line. Self.level seems to be treated as equal to 0 even when it is higher than 0.
self.experience = 0
def level_up(self): #triggered on monster death
xp_required = 15
if self.experience >= xp_required:
self.level += 1
hero = Hero()
I know hero.level_up() is successful because:
print(hero.level)
returns a level that gets correctly updated as appropriate.
I'm guessing either:
self.damage only gets calculated once, then stores that value even after components of it have changed.
or:
There is some kind of issue with calling __init__ values within __init__.
or:
The calculation is done on the class Hero, not the object Hero()
You are right, self.damage is only written to once: In the __init__ constructor which is itself only called once (when the object is created). And at that time, self.level is zero, so self.damage will be always 5.
You have two options:
Either you change your code to always update the damage as well whenever you change the level. In order to avoid spreading the damage calculation logic into multiple places, you would create a helper method to update the damage value:
def update_damage(self):
self.damage = 5 + self.level
def level_up(self):
if self.experience <= 15:
self.level += 1
self.update_damage()
If the damage value is only ever dependent on the level, you could also make it a update_level(new_level) method; but I opted out of that solution here since it’s not uncommon to have some other things affect damage later on as well (e.g. damage potions or whatever).
Or you change damage into a calculated property, so the damage value is calculated whenever you access it. That has the overhead that it needs to be calculated every time instead of the value being stored somewhere, but on the other hand, you have the ultimate truth about the damage value and you do not need to remember to update it elsewhere:
def __init__(self):
self.level = 0
self.experience = 0
#property
def damage(self):
return 5 + self.level
Your guess about self.damage being calculated once is correct. As #Willem Van Onsem mentions, self.damage= 5+self.level is an assignment and not an equation. You will need to manually update it each time you change self.level.
The most suitable approach to me seems to be wrap it into a property such as:
#property
def damage(self):
return 5 + self.level
The way that you are treating equality is more closely related to reactive programming which is often emulated with properties in python.

Refactoring "to hit" values for a game

I'm making a game and one of the methods calculates a character's base hit numbers based on skill values. The method currently calculates each value individually, since each skill can be used at short, medium, and long range.
I originally thought I could combine the skills into a tuple and iterate over it, dynamically creating each hit number. But I don't know if it's actually possible, since I currently have each hit number assigned to it's own variable.
I also thought about creating a method for each range, and passing the tuple as an argument. I could create a new tuple or list with the resulting values and then assign them to the individual variables, but I don't see how it would be any better than do it this way, except that it won't look so copy & pasted.
Here's what I currently have:
def calcBaseHitNumbers(self, dict):
"""Calculate character's base hit numbers depending on skill level."""
self.skill_dict = dict
self.rifle = self.skill_dict.get('CRM', 0)
self.pistol = self.skill_dict.get('PST', 0)
self.big_gun = self.skill_dict.get('LCG', 0)
self.heavy_weapon = self.skill_dict.get('HW', 0)
self.bow = self.skill_dict.get('LB', 0)
#self.skill_tuple = (self.rifle, self.pistol, self.big_gun, self.heavy_weapon,
# self.bow)
#---Short range
## for skill in self.skill_tuple:
## self.base_hit_short = skill * 0.6
self.charAttribs.bhCRM_short = self.rifle * 0.6
self.charAttribs.bhPST_short = self.pistol * 0.6
self.charAttribs.bhHW_short = self.heavy_weapon * 0.6
self.charAttribs.bhLCG_short = self.big_gun * 0.6
self.charAttribs.bhLB_short = self.bow * 0.6
#---Med range
self.charAttribs.bhCRM_med = self.rifle * 0.3
self.charAttribs.bhPST_med = self.pistol * 0.3
self.charAttribs.bhHW_med = self.heavy_weapon * 0.3
self.charAttribs.bhLCG_med = self.big_gun * 0.3
self.charAttribs.bhLB_med = self.bow * 0.3
#---Long range
self.charAttribs.bhCRM_long = self.rifle * 0.1
self.charAttribs.bhPST_long = self.pistol * 0.1
self.charAttribs.bhHW_long = self.heavy_weapon * 0.1
self.charAttribs.bhLCG_long = self.big_gun * 0.1
self.charAttribs.bhLB_long = self.bow * 0.1
How would you refactor this so it's more dynamic?
Edit: I guess what I want to do is something like this:
Have a tuple (like the one I commented out) and iterate over it 3 times, each time making a new value (for each skill) based on the modifier for each particular range. The resulting value is then automatically assigned to it's respective variable.
In my head, it makes sense. But when I actually try to code it, I get lost. The problem, I think, is that this is the first "real" program I've written; all I've done before are small scripts.
This is only the 0.1 version of my program, so it's not critical to refactor it now. However, it seems very un-Pythonic to do this manually and I also want to "future-proof" this in case things change down the road.
It feels like what you really want is a class representing the weapon, with attributes to handle the base values and calculate hit values with various modifiers. Here's a simple example:
SHORT_RANGE = 'S'
MEDIUM_RANGE = 'M'
LONG_RANGE = 'L'
SHORT_RANGE_MODIFIER = 0.6
MEDIUM_RANGE_MODIFIER = 0.3
LONG_RANGE_MODIFIER = 0.1
class Weapon(object):
def __init__(self, code_name, full_name, base_hit_value,
short_range_modifier=None, medium_range_modifier=None,
long_range_modifier=None):
self.code_name, self.full_name = code_name, full_name
self.base_hit_value = base_hit_value
self.range_modifiers = {
SHORT_RANGE: short_range_modifier or SHORT_RANGE_MODIFIER,
MEDIUM_RANGE: medium_range_modifier or MEDIUM_RANGE_MODIFIER,
LONG_RANGE: long_range_modifier or LONG_RANGE_MODIFIER,
}
def hit_value(self, range, modifier=1):
return self.base_hit_value * self.range_modifiers[range] * modifier
From there, you might create instances of Weapon inside your Character object like so:
self.rifle = Weapon('CRM', 'rifle', 5)
self.pistol = Weapon('PST', 'pistol', 10)
And then if, say, the character fires the pistol at short range:
hit_value = self.pistol.hit_value(SHORT_RANGE)
The extra argument to the hit_value() method can be used to pass in character- or situation-specific modifications.
Of course, the next step beyond this would be to directly model the weapons as subclasses of Weapon (perhaps breaking down into specific types of weapons, like guns, bows, grenades, etc., each with their own base values) and add an Inventory class to represent the weapons a character is carrying.
All of this is pretty standard, boring object-oriented design procedure, but for plenty of situations this type of thinking will get you off the ground quickly and provide at least a little bit of basic flexibility.
Lets see if I understand you scenario: each weapon has its own distinct hit point so a rifle may have 1, a heavy weapon may have 2 etc. Then each character has a short, medium and long value to be multiplied by the hit point of the weapon.
You should consider using a Strategy design. That is create a weapon superclass with a hit point property. Create sub class weapons for rifle, pistol, bow etc. I am sure that the differences between the weapons are more than just the hit points.
Then the Character has one or more weapons depending on your gameplay. To calculate the hit point for a particular weapon is as simple as
current_weapon * self.medium
If you decide to add more weapons later on then you do not have to edit your Character code because your character can handle any weapon.
In Pseudo Python
class Weapon
hit = 1
#other properties of weapon
class Rifle(Weapon)
#other properties of Rifle
class Pistol(Weapon)
#other properties of Pistol
class Character
weapon = Rifle()
long=0.6
def calcHit()
return self.long*weapon.hit
john = Character()
john.weapon= Rifle()
john.calcHit
#Vinko: perhaps make calcBaseHitNumbers, do the "if not self.calculatedBase:" check internally, and just no-op if it's been done before. That said, I can't see the pressing need for precalculating this information. But I'm no Python performance expert.
What sense of dynamic do you mean? What is likely to vary - the number of skills, or the weighting factors, the number of ranges (short, med, long) or all of these?
What happens to the (e.g.) bhPST_* values afterwards - do they get combined into one number?
One thing that leaps out is that the list of skills is hardwired in the code - I would be inclined to replace the bh variables with a method
So (please take into account I don't know the first thing about Python :) )
def bh_short(self, key)
skill = self.skill_dict.get(key, 0)
return skill * 0.6
Now you can keep a list of skills that contribute to hit points and iterate over that calling bh_short etc.
Possibly also pass the range (long med short) unto the function, or return all three values - this all depends on what you're going to do next with the calculated hitpoints.
Basically, we need more information about the context this is to be used in
I would have a class for the character's attributes (so you don't have heaps of things in the character class) and a class for a weapon's attributes:
class WeaponAttribute(object):
short_mod = 0.6
med_mod = 0.3
long_mod = 0.1
def __init__(self, base):
self.base = base
#property
def short(self):
return self.base * self.short_mod
#property
def med(self):
return self.base * self.med_mod
#property
def long(self):
return self.base * self.long_mod
class CharacterAttributes(object):
def __init__(self, attributes):
for weapon, base in attributes.items():
setattr(self, weapon, WeaponAttribute(base))
Have a CharacterAttributes object in the character class and use it like this:
# Initialise
self.charAttribs = CharacterAttributes(self.skill_dict)
# Get some values
print self.charAttribs.CRM.short
print self.charAttribs.PST.med
print self.charAttribs.LCG.long

Categories