code:
class aa:
def getDis():
return 0
class bb(aa):
def getDis():
return 10
class cc(aa):
def getDis():
return 15
class d:
def __init__(self,cust):
self.cust = cust
self.memberLevel = aa()
def cgl(self,amt):
if amt ==1:
self.memberLevel = bb()
elif amt ==2:
self.memberLevel = cc()
else:
self.memberLevel = aa()
Well i have these four classes. What i am trying to do is that in my top three classes i have inheritance. In my class d i am trying to call the respective method if the amount is 1 0r 2 . Instead it is giving me this output:
d1 = d('one')
d1.cgl(1)
print(d1.memberLevel)
output:
<__main__.bb object at 0x036D7BF0>
So help required that how can i use overriding technique.
It seems you were hoping to see the console output:10
Because d1.memberLevel is the bb class itself (not the value 10, and not the function that returns the value 10), the console output shows <__main__.bb object at 0x036D7BF0>. This is correct behavior when printing an object directly.
Perhaps your last line of console code should say:
print(d1.memberLevel.getDis())
Another option would be to assign the value within the "d" class:
self.memberLevel = bb().getDis()
And the member methods require "self", e.g.:
def getDis(self):
return 10
Related
I have a class Foo with its instances having a "balance" attribute. I'm designing it in such a way that Foo can track all the balances of its active instances. By active I mean instances that are currently assigned to a declared variable, of part of a List that is a declared variable.
a = Foo(50) # Track this
b = [ Foo(20) for _ in range(5) ] # Track this
Foo(20) # Not assigned to any variable. Do not track this.
Another feature of Foo is that is has an overloaded "add" operator, where you can add two Foo's balances together or add to a Foo's balance by adding it with an int or float.
Example:
x = Foo(200)
x = x + 50
y = x + Foo(30)
Here is my code so far:
from typing import List
class Foo:
foo_active_instances: List = []
def __init__(self, balance: float = 0):
Foo.foo_active_instances.append(self)
self.local_balance: float = balance
#property
def balance(self):
"""
The balance of only this instance.
"""
return self.local_balance
def __add__(self, addend):
"""
Overloading the add operator
so we can add Foo instances together.
We can also add more to a Foo's balance
by just passing a float/int
"""
if isinstance(addend, Foo):
return Foo(self.local_balance + addend.local_balance)
elif isinstance(addend, float | int):
return Foo(self.local_balance + addend)
#classmethod
#property
def global_balance(cls):
"""
Sum up balance of all active Foo instances.
"""
return sum([instance.balance for instance in Foo.foo_active_instances])
But my code has several issues. One problem is when I try to add a balance to an already existing instance, like:
x = Foo(200)
x = x + 50 # Problem: This instantiates another Foo with 200 balance.
y = Foo(100)
# Expected result is 350, because 250 + 100 = 350.
# Result is 550
# even though we just added 50 to x.
print(Foo.global_balance)
Another problem is replacing a Foo instance with None doesn't remove it from Foo.foo_active_instances.
k = Foo(125)
k = None
# Expected global balance is 0,
# but the balance of the now non-existing Foo still persists
# So result is 125.
print(Foo.global_balance)
I tried to make an internal method that loops through foo_active_instances and counts how many references an instance has. The method then pops the instance from foo_active_instance if it doesn't have enough. This is very inefficient because it's a loop and it's called each time a Foo instance is made and when the add operator is used.
How do I rethink my approach? Is there a design pattern just for this problem? I'm all out of ideas.
The weakref module is perfect for this design pattern. Instead of making foo_active_instances a list, you can make it a weakref.WeakSet. This way, when a Foo object's reference count falls to zero (e.g., because it wasn't bound to a variable), it will be automatically removed from the set.
class Foo:
foo_active_instances = weakref.WeakSet()
def __init__(self, balance: float = 0) -> None:
Foo.foo_active_instances.add(self)
...
In order to add Foo objects to a set, you'll have to make them hashable. Maybe something like
class Foo:
...
def __hash__(self) -> int:
return hash(self.local_balance)
You can use inspect to check if the __init__ or __add__ methods have been called as part of an assignment statement. Additionally, you can keep a default parameter in __init__ to prevent increasing your global sum by the value passed to it when creating a new Foo object from __add__:
import inspect, re
def from_assignment(frame):
return re.findall('[^\=]\=[^\=]', inspect.getframeinfo(frame).code_context[0])
class Foo:
global_balance = 0
def __init__(self, balance, block=False):
if not block and from_assignment(inspect.currentframe().f_back):
Foo.global_balance += balance
self.local_balance = balance
def __add__(self, obj):
if from_assignment(inspect.currentframe().f_back) and not hasattr(obj, 'local_balance'):
Foo.global_balance += obj
return Foo(getattr(obj, 'local_balance', obj), True)
a = Foo(50)
b = [Foo(20) for _ in range(5)]
Foo(20)
print(Foo.global_balance) #150
x = Foo(200)
x = x + 50
y = Foo(100)
print(Foo.global_balance) #350
I'm studying OOP in Python and I'm making a game with 5 dices. I made the Dado object to represent my dices and the GeraDado object to actually create the Dado object. If I create an object such as d1 = GeraDado() I can print its value using the d1.valor() method but if I try to append its value to a list or dictionary it returns None. If I do print(d1) it returns the object's location in memmory and not the value. How can I make it return the d1 = GeraDado() value instead of using the d1.valor() method, that just prints the value on screen?
from random import randint
class Dado:
def __init__(self, valor):
self.__valor = valor
def valor(self):
print(self.__valor)
class GeraDado:
def __init__(self):
self.__dado = Dado(randint(1,6))
def dado(self):
self.__dado.valor()
d1 = GeraDado()
print(d1)
Your Dado.valor and GeraDado.dado methods only print the value of the die, they don't actually return it, instead, make them return the value using the return statement:
Dado.valor:
def valor(self):
return self.__valor
GeraDado.dado:
def dado(self):
return self.__dado.valor()
Two little suggestions:
class Dado:
# ...
def valor(self):
return self.__valor
# 1. return stuff!
# Don't fall in love with print. It's just a debugging side-effect
class GeraDado:
# ...
def dado(self):
return self.__dado.valor() # 1. again: return!
# 2. implement a __str__ method.
# This is your class's representation that is used by print
def __str__(self):
return str(self.dado())
>>> d1 = GeraDado()
>>> print(d1)
1
I have a function that creates a player object but when referencing the object, I get a NameError. I think it is happening due to local scope but global should fix it...
I just started out OOP and this code is working in the python shell but it is not working in script mode.
endl = lambda a: print("\n"*a)
class Score:
_tie = 0
def __init__(self):
self._name = ""
self._wins = 0
self._loses = 0
def get_name(self):
print
self._name = input().upper()
def inc_score(self, wlt):
if wlt=="w": self._wins += 1
elif wlt=="l": self._loses += 1
elif wlt=="t": _tie += 1
else: raise ValueError("Bad Input")
def player_num(): #Gets number of players
while True:
clear()
endl(10)
print("1 player or 2 players?")
endl(5)
pnum = input('Enter 1 or 2: '.rjust(55))
try:
assert int(pnum) == 1 or int(pnum) == 2
clear()
return int(pnum)
except:
print("\n\nPlease enter 1 or 2.")
def create_player(): #Creates players
global p1
p1 = Score()
yield 0 #stops here if there is only 1 player
global p2
p2 = Score()
def pr_(): #testing object
input(p1._wins)
input(p2._wins)
for i in range(player_num()):
create_player()
input(p1)
input(p1._wins())
pr_()
wherever I reference p1 I should get the required object attributes but I'm getting this error
Traceback (most recent call last):
File "G:/Python/TicTacTwo.py", line 83, in <module>
input(p1)
NameError: name 'p1' is not defined
Your issue is not with global but with the yield in create_player(), which turns the function into a generator.
What you could do:
Actually run through the generator, by executing list(create_player()) (not nice, but works).
But I suggest you re-design your code instead, e.g. by calling the method with the number of players:
def create_player(num): #Creates players
if num >= 1:
global p1
p1 = Score()
if num >= 2:
global p2
p2 = Score()
If you fix this issue, the next issues will be
1) input(p1) will print the string representation of p1 and the input will be lost, you probably want p1.get_name() instead.
2) input(p1._wins()) will raise TypeError: 'int' object is not callable
I will redesign the app to introduce really powerful python constructs that may help you when getting into OOP.
Your objects are going to represent players, then don't call them Score, call them Player.
Using _tie like that makes it a class variable, so the value is shared for all the players. With only two participants this may be true but this will come to hurt you when you try to extend to more players. Keep it as a instance variable.
I am a fan of __slots__. It is a class special variable that tells the instance variables what attributes they can have. This will prevent to insert new attributes by mistake and also improve the memory needed for each instance, you can remove this line and it will work but I suggest you leave it. __slots__ is any kind of iterable. Using tuples as they are inmutable is my recomendation.
Properties are also a really nice feature. They will act as instance attribute but allow you to specify how they behave when you get the value (a = instance.property), assign them a value (instance.property = value), or delete the value (del instance.property). Name seems to be a really nice fit for a property. The getter will just return the value stored in _name, the setter will remove the leading and trailing spaces and will capitalize the first letter of each word, and the deletter will set the default name again.
Using a single function to compute a result is not very descriptive. Let's do it with 3 functions.
The code could look like this:
# DEFAULT_NAME is a contant so that we only have to modify it here if we want another
# default name instead of having to change it in several places
DEFAULT_NAME = "Unknown"
class Player:
# ( and ) are not needed but I'll keep them for clarity
__slots__ = ("_name", "_wins", "_loses", "_ties")
# We give a default name in case none is provided when the instance is built
def __init__(self, name=DEFAULT_NAME):
self._name = name
self._wins = 0
self._loses = 0
self._ties = 0
# This is part of the name property, more specifically the getter and the documentation
#property
def name(self):
""" The name of the player """
return self._name
# This is the setter of the name property, it removes spaces with .strip() and
# capitalizes first letters of each word with .title()
#name.setter
def name(self, name):
self._name = name.strip().title()
# This is the last part, the deleter, that assigns the default name again
#name.deleter
def name(self):
self._name = DEFAULT_NAME
def won(self):
self._wins += 1
def lost(self):
self._loses += 1
def tied(self):
self._ties += 1
Now that's all we need for the player itself. The game should have a different class where the players are created.
class Game:
_min_players = 1
_max_players = 2
def __init__(self, players):
# Check that the number of players is correct
if not(self._min_players <= players <= self._max_players):
raise ValueError("Number of players is invalid")
self._players = []
for i in range(1, players+1):
self._players.append(Player(input("Insert player {}'s name: ".format(i))))
#property
def players(self):
# We return a copy of the list to avoid mutating the inner list
return self._players.copy()
Now the game would be created as follows:
def new_game():
return Game(int(input("How many players? ")))
After that you would create new methods for the game like playing matches that will call the players won, lost or tied method, etc.
I hope that some of the concepts introduced here are useful for you, like properties, slots, delegating object creation to the owner object, etc.
This question already has answers here:
How to avoid having class data shared among instances?
(7 answers)
Closed 3 years ago.
I've the following class structure:
class StarCount:
one_stars = 0
two_stars = 0
three_stars = 0
four_stars = 0
five_stars = 0
class OrientationAnalysis:
straight = StarCount()
bisexual = StarCount()
gay = StarCount()
class GenderAnalysis:
men = OrientationAnalysis()
women = OrientationAnalysis()
I've written the following code:
genderanalysis = GenderAnalysis()
genderanalysis.men.straight.five_stars = 100
print genderanalysis.men.straight.five_stars # outputs 100
print genderanalysis.women.straight.five_stars # this is also 100
Why genderanalysis.women.straight.five_stars is also updated? I've checked the genderanalysis.women.gay.five_stars also but it's not updated?
When you declare some variables like this:
class StarCount:
one_stars = 0
two_stars = 0
three_stars = 0
four_stars = 0
five_stars = 0
These variables become class variables. Class variables are variables that are shared by all instances of a class. So when you updated genderanalysis.men.straight.five_stars, it actually updated StarCount.five_stars and as genderanalysis.women.straight.five_stars also points to the same variable, it seemed to have updated too.
I think what you are looking for are instance variables. You can declare them like this:
class StarCount:
def __init__(self):
self.one_stars = 0
self.two_stars = 0
self.three_stars = 0
self.four_stars = 0
self.five_stars = 0
Edit
Why genderanalysis.women.gay.five_stars is not updated?
What happens is that before you updated any variable of genderanalysis object, all of the variables were pointing to the variables of StarCount class. As you can see they have same id:
print(id(StarCount.five_stars)) # prints '94016229389744'
print(id(genderanalysis.men.straight.five_stars)) # prints '94016229389744'
print(id(genderanalysis.women.gay.five_stars)) # prints '94016229389744'
But when you changed genderanalysis.men.straight.five_stars, the reference/pointer got replaced with your provided value, in this case 100. You can see difference in their id's:
print(id(StarCount.five_stars)) # prints '94016229389744'
print(id(genderanalysis.men.straight.five_stars)) # prints '94016229391328', see the difference?
So now genderanalysis.men.straight.five_stars does not point to StarCount.five_stars, rather it points to OrientationAnalysis.straight.five_stars. Once again, let's check their id's:
print(id(OrientationAnalysis.straight.five_stars)) # prints '94016229391328'
print(id(genderanalysis.men.straight.five_stars)) # prints '94016229391328', same right?
Now onto your question, at this point genderanalysis.women.gay.five_stars is still untouched so it points to StarCount.five_stars and so it still prints 0. Change StarCount.five_stars and you can see the change reflecting in genderanalysis.women.gay.five_stars.
StarCount.five_stars = 101
print(genderanalysis.women.gay.five_stars) # prints `101`
Define the values in the init methods of the classes, so that they become attached to intance objects, not the class object itself.
class StarCount:
def __init__(self):
self.one_stars = 0
self.two_stars = 0
self.three_stars = 0
self.four_stars = 0
self.five_stars = 0
class OrientationAnalysis:
def __init__(self):
self.straight = StarCount()
self.bisexual = StarCount()
self.gay = StarCount()
class GenderAnalysis:
def __init__(self):
self.men = OrientationAnalysis()
self.women = OrientationAnalysis()
genderanalysis = GenderAnalysis()
genderanalysis.men.straight.five_stars = 100
print genderanalysis.men.straight.five_stars # outputs 100
print genderanalysis.women.straight.five_stars # this is now 0
your attributes should not be class attributes but instance attributes instead. this would be a start for you:
class StarCount:
def __init__(self, five_stars=0):
self.five_stars = five_stars
# ...
class OrientationAnalysis:
def __init__(self):
self.straight = StarCount()
# ...
class GenderAnalysis:
def __init__(self):
self.men = OrientationAnalysis()
self.women = OrientationAnalysis()
you’re almost there— you are referencing and modifying class variables rather than instance variables.
You need an __init__(self) method, and to create all the attributes on self
I have a simple class that stores simple data. The class is as follows.
class DataFormater:
def __init__(self, N, P, K, price):
self.N = N
self.P = P
self.K = K
self.price = price
The code that calls this class is
from DataFormater import DataFormater
#global variables
ObjectList = [0,1,2,3,4,5,6,7,8,9,10,
11,12,13,14,15,16,17,18,19,20,
21,22,23,24,25,26,27,28,29,30,
31,32,33,34,35,36,37,38,39,40,
41,42,43,44,45,46,47,48,49,50]
ObjectListCounter = 0
# main
print "enter you N-P-K values, followed by a coma, then the price"
print "example ----> 5 5 5 %50 "
print "return as many values as you want to sort, then enter, 'done!' when done."
while True:
RawData = raw_input()
if RawData == 'done!':
break
else:
ObjectList[ObjectListCounter] = DataFormater
ObjectList[ObjectListCounter].N = int(RawData[0])
# very simple test way of putting first indice in ObjectList[ObjectListCounter].N
ObjectListCounter += 1
print ObjectList[0].N
print ObjectList[1].N
My idea is that ObjectList[0] would create that object '1' that I could call with 1.N
But, when I call these, it seems that I have overwritten the previous instances.
this is what prints...
return as many values as you want to sort, then enter, 'done!' when done.
12
1
done!
1
1
Thanks so much! And I know that my post is messy, I don't exactly know how to make it more "pretty"
So, it looks like you are assigning the actual class (instead of an instance of the class) in your loop. Where you do this:
ObjectList[ObjectListCounter] = DataFormater
I think what you actually want is this
ObjectList[ObjectListCounter] = DataFormater(...insert args here....)
EDIT to address the comments:
Your class init method looks like this:
def __init__(self, N, P, K, price):
That means that to create an instance of your class, it would look like this:
my_formater = DataFormater(1, 2, 3, 4)
You would then be able to access my_formater.N which would have a value of 1.
What you are trying to do instead is access a CLASS level attribute, DataFormater.N. This is generally used in situations where you have a constant variable that does not change between instances of the class. For example:
class DataFormater():
CONSTANT_THING = 'my thing that is always the same for every instance'
You would then be able to access that variable directly from the class, like this:
DataFormater.CONSTANT_THING
I hope that clears things up.