Have a class act as a dict? - python

I have a class defined like so:
class GameState:
def __init__(self, state=None):
if state is None:
self.fps = 60
self.speed = 1
self.bounciness = 0.9
self.current_level = None
self.next_frame_time = 0
self.init_time = 0
self.real_time = 0
self.game_time = 0
self.game_events = []
self.real_events = []
else:
# THIS being the key line:
self.__dict__.update(**state)
Is there an interface I can define, such that this works (i.e. the ** operator works on my class):
>>> a = GameState()
>>> b = GameState(a)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: update() argument after ** must be a mapping, not GameState
Essentially, I want b to take on all of the attributes of a.
I didn't think it would work, but I tried defining __getitem__ without any luck.
EDIT: I want to avoid using b's __dict__, as I want to also be able to pass a dictionary as an argument, and potentially use ** on GameState objects elsewhere.

let GameState inherit from dict :
class GameState(dict)
and rewrite the __setattr function like this :
def __setattr__(self,name,value) :
self.__dict__[name] = value
self[name] = value

in order for **obj to work, you have to implement (or inherit) the __getitem__() and keys() methods.
def __getitem__(self, item):
return self.__dict__[item] # you maybe should return a copy
def keys(self):
return self.__dict__.keys() # you could filter those

you could do that by updating the b's dict with that of a when creating b. Try this out:
class GameState:
def __init__(self, state=None):
if state is None:
self.fps = 60
self.speed = 1
self.bounciness = 0.9
self.current_level = None
self.next_frame_time = 0
self.init_time = 0
self.real_time = 0
self.game_time = 0
self.game_events = []
self.real_events = []
else:
if type(state) is dict:
self.__dict__.update(**state)
else:
self.__dict__.update(**state.__dict__)
a = GameState()
b = GameState(a)
you might want to create a deepcopy of the dict because you have a list object as part of the attributes. This is safer as there is no sharing of objects.

Related

What dunder method to use in Python to return an attribute of the object?

I am trying to create a "CumulativeMovingAverage" class. That's what I did:
class CumulativeMovingAverage():
cma = None
n = 0
def add(self, *args):
if self.cma is None:
self.cma = args[0]
else:
self.cma = (args[0] + self.n*self.cma) / (self.n+1)
self.n += 1
return None
def __call__(self):
return self.cma
It works like this:
a = CumulativeMovingAverage()
a.add(2)
a.add(4)
a.cma ==> 3
a() ==> 3
I would like to overwrite a dunder method such that
a ==> 3
and also
b = a + 100
b ==> 103
That is, without having to call a with parenthesis. Is it possible? What dunder should I overwrite?

Access variables inside a method

How can I get access to a variable inside a method outside the class?
class alpha():
c = 9
def __inobjectit__(self, k):
self.k = k
def amo(self):
self.reward = 32
I know that to get access to the value of c outside the class I can simply use the following code:
a = alpha.c
But how can I get access to the value of the self.reward outside the class?
If you want (self.reward) to be a constant, you should put it in the init method, like this:
class alpha():
def __init__(self,k):
self.k = k
self.reward = 32
a = alpha("Some argument")
value = a.reward
print(value)
Then if you want to change the value of .reward you do like this:
class alpha():
def __init__(self,k):
self.k = k
self.reward = 32
a = alpha("Some argument")
value = a.reward
print(value)
a.reward = 10
value = a.reward
print(value)

Change attribute after each object calling

I'm trying to figure out how to change some value after each call of the object.
I thougt that call() function is executed after each call.
This should be a simple counter class which decreases value attribute after being called.
class counter():
def __init__(self,value):
self.value = value
def __call__(self):
self.value -= 1
count = counter(50)
print count.value
print count.value
>> 50
>> 50 <-- this should be 49
What am I doing wrong?
If you're not committed to classes, you could use a function and abuse using mutable-types-as-default-initializers:
def counter(init=None, container=[0]):
container[0] -= 1
if init is not None: container[0] = init
return container[0]
x = counter(100)
print(x) # 100
print( counter() ) # 99
print( counter() ) # 98
print( counter() ) # 97
# ...
Call counter with a single argument to set/initialize the counter. Since initialization is actually the first call to the function, it will return that number.
Call counter with no arguments to get the "next value".
(Very similar to what I suggested here)
Alternatively, for a syntax closer to what you had in your question, use properties:
class Counter(object):
def __init__(self, init):
self.val = init
#property
def value(self):
val = self.val
self.val -= 1
return val
count = Counter(50)
print(count.value) # 50
print(count.value) # 49
print(count.value) # 48
print(count.value) # 47
#...
Here, you're creating a Counter object called count, then every time you call count.value it returns the current value and prepares itself for a future call by decrementing it's internal val attribute.
Again, the first time you request the value attribute, it returns the number you initialized it with.
If, for some reason, you want to "peek" at what the next call to count.value will be, without decrementing it, you can look at count.val instead.
__call__ is only invoked when you call the object using ()
To invoke this behaviour you'd have to do
class counter():
def __init__(self,value):
self.value = value
def __call__(self):
print 'called'
self.value -= 1
count = counter(50)
print count.value
count()
print count.value
This may not be exactly what you wanted to do.
Use the property decorator
class counter:
def __init__(self, value):
self._value = value + 1
#property
def value(self):
self._value -= 1
return self._value
count = Counter(50)
print(count.value) # 50
print(count.value) # 49
Alternatly, you could use a closure:
def Counter(n):
n += 1
def inner():
n -= 1
return n
return inner
Though this has to be called every time you want to use it
count1 = Counter(50)
count2 = Counter(50)
print(count1()) # 50
print(count1()) # 49
print(count2()) # 50
print(count2()) # 49
print(count1()) # 48
Defining a custom call() method in the meta-class allows custom behaviour when the class is called, e.g. not always creating a new instance.As no new class instance is created call gets called instead of init.So do this to get the desired result
print count.value
count()
print count.value

Calling a method from the same class

I'm writing a class for a simple game of 4 in a row, but I'm running into a problem calling a method in the same class. Here's the whole class for the sake of completeness:
class Grid:
grid = None
# creates a new empty 10 x 10 grid
def reset():
Grid.grid = [[0] * 10 for i in range(10)]
# places an X or O
def place(player,x,y):
Grid.grid[x][y] = player
# returns the element in the grid
def getAt(x,y):
return Grid.grid[x][y]
# checks for wins in a certain direction
def checkLine(player,v,count,x,y):
x = x+v[0]
y = y+v[1]
if x < 0 or x > 9:
return
if y < 0 or y > 9:
return
if Grid.grid[x][y] == p:
count = count+1
if count == 4:
return True
checkLine(player,v,count,x,y)
return False
# returns the number of the player that won
def check():
i = 'i'
for x in range(0,10):
for y in range(0,10):
if Grid.grid[x][y] > 0:
p = Grid.grid[x][y]
f = checkLine(p,0,array(i,[1,0]),x,y)
if f:
return p
f = checkLine(p,0,array(i,[0,1]),x,y)
if f:
return p
f = checkLine(p,0,array(i,[1,1]),x,y)
if f:
return p
f = checkLine(p,0,array(i,[-1,0]),x,y)
if f:
return p
f = checkLine(p,0,array(i,[0,-1]),x,y)
if f:
return p
f = checkLine(p,0,array(i,[-1,-1]),x,y)
if f:
return p
f = checkLine(p,0,array(i,[1,-1]),x,y)
if f:
return p
f = checkLine(p,0,array(i,[-1,1]),x,y)
if f:
return p
return 0
reset = staticmethod(reset)
place = staticmethod(place)
getAt = staticmethod(getAt)
check = staticmethod(check)
checkLine = staticmethod(checkLine)
I'm trying to call checkLine() from check(), but I get the error "NameError: global name 'checkLine' is not defined". When I call Grid.checkLine() instead, I get "TypeError: 'module' object is not callable"
How do I call checkLine()?
EDIT:
#beer_monk
class Grid(object):
grid = None
# creates a new empty 10 x 10 grid
def reset(self):
Grid.grid = [[0] * 10 for i in range(10)]
# places an X or O
def place(self,player,x,y):
Grid.grid[x][y] = player
# returns the element in the grid
def getAt(self,x,y):
return Grid.grid[x][y]
# checks for wins in a certain direction
def checkLine(self,player,v,count,x,y):
x = x+v[0]
y = y+v[1]
if x < 0 or x > 9:
return
if y < 0 or y > 9:
return
if Grid.grid[x][y] == p:
count = count+1
if count == 4:
return True
checkLine(self,player,v,count,x,y)
return False
# returns the number of the player that won
def check(self):
i = 'i'
for x in range(0,10):
for y in range(0,10):
if Grid.grid[x][y] > 0:
p = Grid.grid[x][y]
for vx in range(-1,2):
for vy in range(-1,2):
f = self.checkLine(p,0,array(i,[vx,vy]),x,y)
if f:
return p
return 0
reset = staticmethod(reset)
place = staticmethod(place)
getAt = staticmethod(getAt)
check = staticmethod(check)
checkLine = staticmethod(checkLine)
Get rid of the class. Use plain functions and module level variable for grid.
The class is not helping you in any way.
PS. If you really want to call checkline from within the class, you'd call Grid.checkline. For example:
class Foo:
#staticmethod
def test():
print('Hi')
#staticmethod
def test2():
Foo.test()
Foo.test2()
prints
Hi
Syntax:
class_Name.function_Name(self)
Example:
Turn.checkHoriz(self)
A reworked example (hopefully showing a better use of classes!)
import itertools
try:
rng = xrange # Python 2.x
except NameError:
rng = range # Python 3.x
class Turn(object):
def __init__(self, players):
self.players = itertools.cycle(players)
self.next()
def __call__(self):
return self.now
def next(self):
self.now = self.players.next()
class Grid(object):
EMPTY = ' '
WIDTH = 10
HEIGHT = 10
WINLENGTH = 4
def __init__(self, debug=False):
self.debug = debug
self.grid = [Grid.EMPTY*Grid.WIDTH for i in rng(Grid.HEIGHT)]
self.player = Turn(['X','O'])
def set(self, x, y):
if self.grid[y][x]==Grid.EMPTY:
t = self.grid[y]
self.grid[y] = t[:x] + self.player() + t[x+1:]
self.player.next()
else:
raise ValueError('({0},{1}) is already taken'.format(x,y))
def get(self, x, y):
return self.grid[y][x]
def __str__(self):
corner = '+'
hor = '='
ver = '|'
res = [corner + hor*Grid.WIDTH + corner]
for row in self.grid[::-1]:
res.append(ver + row + ver)
res.append(corner + hor*Grid.WIDTH + corner)
return '\n'.join(res)
def _check(self, s):
if self.debug: print("Check '{0}'".format(s))
# Exercise left to you!
# See if a winning string exists in s
# If so, return winning player char; else False
return False
def _checkVert(self):
if self.debug: print("Check verticals")
for x in rng(Grid.WIDTH):
winner = self._check([self.get(x,y) for y in rng(Grid.HEIGHT)])
if winner:
return winner
return False
def _checkHoriz(self):
if self.debug: print("Check horizontals")
for y in rng(Grid.HEIGHT):
winner = self._check([self.get(x,y) for x in rng(Grid.WIDTH)])
if winner:
return winner
return False
def _checkUpdiag(self):
if self.debug: print("Check up-diagonals")
for y in rng(Grid.HEIGHT-Grid.WINLENGTH+1):
winner = self._check([self.get(d,y+d) for d in rng(min(Grid.HEIGHT-y, Grid.WIDTH))])
if winner:
return winner
for x in rng(1, Grid.WIDTH-Grid.WINLENGTH+1):
winner = self._check([self.get(x+d,d) for d in rng(min(Grid.WIDTH-x, Grid.HEIGHT))])
if winner:
return winner
return False
def _checkDowndiag(self):
if self.debug: print("Check down-diagonals")
for y in rng(Grid.WINLENGTH-1, Grid.HEIGHT):
winner = self._check([self.get(d,y-d) for d in rng(min(y+1, Grid.WIDTH))])
if winner:
return winner
for x in rng(1, Grid.WIDTH-Grid.WINLENGTH+1):
winner = self._check([self.get(x+d,d) for d in rng(min(Grid.WIDTH-x, Grid.HEIGHT))])
if winner:
return winner
return False
def isWin(self):
"Return winning player or False"
return self._checkVert() or self._checkHoriz() or self._checkUpdiag() or self._checkDowndiag()
def test():
g = Grid()
for o in rng(Grid.WIDTH-1):
g.set(0,o)
g.set(Grid.WIDTH-1-o,0)
g.set(Grid.WIDTH-1,Grid.HEIGHT-1-o)
g.set(o,Grid.HEIGHT-1)
print(g)
return g
g = test()
print g.isWin()
Unlike java or c++, in python all class methods must accept the class instance as the first variable. In pretty much every single python code ive seen, the object is referred to as self. For example:
def reset(self):
self.grid = [[0] * 10 for i in range(10)]
See http://docs.python.org/tutorial/classes.html
Note that in other languages, the translation is made automatically
There are multiple problems in your class definition. You have not defined array which you are using in your code. Also in the checkLine call you are sending a int, and in its definition you are trying to subscript it. Leaving those aside, I hope you realize that you are using staticmethods for all your class methods here. In that case, whenever you are caling your methods within your class, you still need to call them via your class's class object. So, within your class, when you are calling checkLine, call it is as Grid.checkLine That should resolve your NameError problem.
Also, it looks like there is some problem with your module imports. You might have imported a Module by name Grid and you have having a class called Grid here too. That Python is thinking that you are calling your imported modules Grid method,which is not callable. (I think,there is not a full-picture available here to see why the TypeError is resulting)
The best way to resolve the problem, use Classes as they are best used, namely create objects and call methods on those objects. Also use proper namespaces. And for all these you may start with some good introductory material, like Python tutorial.
Instead of operating on an object, you are actually modifying the class itself. Python lets you do that, but it's not really what classes are for. So you run into a couple problems
-You will never be able to make multiple Grids this way
the Grid can't refer back to itself and e.g. call checkLine
After your grid definition, try instantiating your grid and calling methods on it like this
aGrid = Grid()
...
aGrid.checkLine()
To do that you, you first need to modify all of the method definitions to take "self" as your first variable and in check, call self.checkLine()
def check(self):
...
self.checkLine()
...
Also, your repeated checking cries out for a FOR loop. You don't need to write out the cases.
Java programmer as well here, here is how I got it to call an internal method:
class Foo:
variable = 0
def test(self):
self.variable = 'Hi'
print(self.variable)
def test2(self):
Foo.test(self)
tmp = Foo()
tmp.test2()

Python __init__ issue: unbound method __init__() must be called with Bank instance as first argument (got int instance instead)

class Teller(object):
def __init__(self):
self.occupied = False
self.timeLeft = 0
self.totTime
def occupy(self, timeOcc):
self.occupied = True
self.timeLeft = timeOcc
def nextMin(self):
self.timeLeft -= 1
self.totTime += 1
if self.timeLeft == 0:
self.occupied = False
class Bank(object):
def __init__(numTellers, hoursOpen):
self.tellers = []
self.timeWaited = 0
self.clientsWaiting = []
for x in xrange(numTellers):
tempTeller = Teller.__init__()
self.tellers.append(tempTeller)
self.minutesOpen = hoursOpen * 60
def tellerOpen(self):
for x in xrange(len(self.tellers)):
if not self.tellers[x].occupied:
return x+1
return 0
def runSim(self, queueInput): #queueInput is a list of tuples (time, timeAtTeller)
simTime = self.minutesOpen
totCli = 0
timeToNext = queueInput[0][0]
timeAtNext = queueInput[0][1]
queueInput.pop(0)
self.clientsWaiting.append([timeToNext, timeAtNext])
while simTime > 0:
for person in self.clientsWaiting:
if person[0]:
person -= 1
if not self.clientsWaiting[len(self.clientsWaiting)-1][0]:
timeToNext = queueInput[0][0]
timeAtNext = queueInput[0][1]
queueInput.pop(0)
self.clientsWaiting.append([timeToNext, timeAtNext])
remove = 0
for x in xrange (len(self.clientsWaiting)-1):
if tellerOpen() and not self.clientsWaiting[x][0]:
self.tellers[tellerOpen()].occupy(self.clientsWaiting[x][0])
totCli += 1
remove += 1
elif not tellerOpen() and not self.clientsWaiting[x][0]:
self.timeWaited += 1
for x in xrange(remove):
self.clientsWaiting.pop(x)
print """The total time spent in the queue by all clients was %d minutes. The total number of clients today was %d. The average waiting time was %d mins""" % (self.timeWaited, totCli, self.timeWaited / totCli)\
if __name__ == '__main__':
inp = raw_input()
tList = inp.split('\n')
qList = []
for item in tList:
tList = item.split(' ')
qList.append((tList[0], tList[1]))
virtBank = Bank.__init__(3, 7)
bank.runSim(qList)
This results in this error:
> TypeError: unbound method __init__() must be called with Bank instance as first argument (got int instance instead)
I don't see what I've dont wrong. Any advice would be appreciated.
The only important parts, I think, are the Bank class __init__ and the call virtBank = Bank.__init__(3, 7)
2 points to make here:
You shouldn't be calling __init__ directly, it's a magic method which is invoked when you construct an object like this:
virtBank = Bank(3, 7)
The instance is implicitly passed to the constructor, but it must be explicitly received, like this:
def __init__(self, numTellers, hoursOpen):
# ...

Categories