regarding python objects and their methods - python

I am currently trying to code a text-based adventure in pure python. Therefore I have a Room class which looks kinda like this (shortened):
class Room1(Room):
def __init__(self):
Room.__init__(self)
self.init_objects()
self.description = """Looks like a living room. There's a lamp, a
cupboard, and
a door to your right. On the floor, there's a carpet."""
self.door_state = 1
self.carpet_state = 0
self.images = [(text-based images)]
def init_objects(self):
self.objects = {"door" : Door1(),
"frontdoor" : FrontDoor(),
"carpet" : Carpet(),
"lamp" : Lamp1(),
"clock" : Clock(),
"escritoire" : Escritoire(),
"booklet" : Booklet(),
"screws" : Screws(),
"trapdoor" : TrapDoor()}
def update_image(self):
IMG_MAPPER = {(0, 0, 0) : 0,
(0, 0, 1) : 1,
(1, 0, 0) : 2,
(1, 0, 1) : 3,
(1, 1, 1) : 4,
(1, 1, 0) : 5,
(0, 1, 1) : 6,
(0, 1, 0) : 7}
key = (self.objects["door"].state, self.objects["carpet"].state)
self.img = img_mapper[key]
My problem is with the Room's update_image() method. I need a mapper to be stored there to figure out the right image according to the object's states (opened / closed), and if I put this mapper at the start of the method, this dict is read and constructed by python everytime the method is called, is that right? So should I rather store this mapper dict as an instance variable like self.img_mapper_dict = {(0, 0, 0) : 0, ...}?
Any ideas on that anyone perhaps?

You are correct in assuming that the way your current code is structure, IMAGE_MAPPER would be assigned to every single time the update_image() method is called.
Now, since this is a static mapping that doesn't change over time, this is not a problem from a functionality standpoint - it's not like you're resetting a value that should be tracked between calls to update_image().
Performance wise, in this particular case, the drawbacks will also be absolutely negligible.
But it would probably make sense from a purely logical standpoint to make this mapping a class attribute:
class Room1(Room):
IMG_MAPPING = {(0, 0, 0) : 0,
(0, 0, 1) : 1,
(1, 0, 0) : 2,
(1, 0, 1) : 3,
(1, 1, 1) : 4,
(1, 1, 0) : 5,
(0, 1, 1) : 6,
(0, 1, 0) : 7}
def __init__(self):
# ...
def update_image(self):
# ...
self.img = Room1.IMG_MAPPING[key]
I'm recommending a class attribute instead of an instance attribute (or instance member) because that mapping is going to stay the same for all instances of Room1 (right? I'm assuming every instance of Room1 would have exactly the same layout, just different door states etc..). So it doesn't depend on any state of the instance, but it's a property of the class instead.
Also note that the class attribute is accessed differently: Room1.IMG_MAPPING instead of self.IMG_MAPPING. This also reflects the fact that it's not dependent on the instance.

The problem I think you're having is that img_mapper resets to the specified state every time update_image() is called. This is because the member function has its own scope, so when it is completed it deletes img_mapper and has to remake it next time the update function is called. Storing as a member variable would get rid of this problem.

First problem I see is that your IMG_MAPPER dict has three-tuple keys. But you are accessing it by providing a two-tuple.

Related

Recursive function not refreshing a list inside a loop [duplicate]

This question already has answers here:
"Least Astonishment" and the Mutable Default Argument
(33 answers)
Closed 1 year ago.
I have the following class:
class Node:
def __init__(self, id, parent):
self.id = id
self.parent = parent
Some objects of the node class:
n0 = Node(0, None)
n1 = Node(1, n0)
n2 = Node(2, n1)
n3 = Node(3, n2)
And a recursive function to get an hierarchy tree:
def get_hierarchy(node, hierarchy_list=[]):
parent = node.parent
if parent:
hierarchy_list.append(parent)
return get_hierarchy(parent, hierarchy_list)
else:
return ', '.join(str(node.id) for node in reversed(hierarchy_list))
If I call the that function outside a loop it works as expected:
hierarchy = get_hierarchy(n3)
print(hierarchy)
# 0, 1, 2
But if I call that function inside a loop, the hierarchy_list varible is not refreshed on each loop:
for i in range(5):
nodes = [n0, n1, n2]
hierarchy = get_hierarchy(n3)
print(hierarchy)
# 0, 1, 2
# 0, 1, 2, 0, 1, 2
# 0, 1, 2, 0, 1, 2, 0, 1, 2
# 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2
# 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2
And if I force the hierarchy_list to be empty on each interaction, it works as expected:
for i in range(5):
nodes = [n0, n1, n2]
hierarchy = get_hierarchy(n3, []) # forcing to be empty
print(hierarchy)
# 0, 1, 2
# 0, 1, 2
# 0, 1, 2
# 0, 1, 2
# 0, 1, 2
Why python does not refreshes the hierarchy_list on each interaction since I am calling the get_hierarchy function outside get_hierarchy's scope, and if I force it to be a new list it works. And what is the reason for this behavior?
I think the reason is that you are using a mutable argument as the default value in the function, which is a bad idea. The list is initiated only once when the function is defined, not re-initiated every-time you call the function.
The default value of the function argument points to the same list everytime you call that function, so the append method keeps adding to the same list. When you force it to be an empty list, the argument points to a new empty list avoiding the issue you are having.
Have a look here for the same explanation.

How to save state of an iterator in python?

I have a very big iterator. due to lack of resources (Network, Memory and Time), It's not possible for me to execute my program at one step.
So I thought it would be nice if I run my program till 10000th element in iterator and then save its state. next time I run program it continues from 10001st element in iterator.
here is the code I use:
import itertools
import requests
POSSIBLE_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
URL = "URL?key={code}"
all_possible = itertools.combinations_with_replacement(POSSIBLE_CHARS, 29)
counter = itertools.count(start=1)
for c in all_possible:
print("Try {}:".format(next(counter)), c)
c_url = URL.format(code=c)
resp = requests.get(c_url)
if resp.status_code == 200:
print("C found:", c)
with open(c+".gif", 'b') as f:
f.write(resp.content)
this link shows how to continue iterator inside a single execution. but what I want is to stop program and execute again.
So I thought it would be nice if I run my program till 10000th element in iterator and then save its state. next time I run program it continues from 10001st element in iterator.
You're in luck, because itertools.combinations_with_replacement objects have APIs allowing to set state. This is used by the copy module in Python, but there is no reason you can't also hook in.
For example the 10,001th item would be this:
>>> all_possible = itertools.combinations_with_replacement(POSSIBLE_CHARS, 29)
>>> for i in range(10_000):
... next(all_possible)
...
>>> "".join(next(all_possible))
'aaaaaaaaaaaaaaaaaaaaaaaaaafwI'
To "fast-forward" to this item in a fresh instance, it would be:
>>> new_iterator = itertools.combinations_with_replacement(POSSIBLE_CHARS, 29)
>>> state = (0,)*26 + (5, 22, 33)
>>> new_iterator.__setstate__(state)
>>> "".join(next(new_iterator))
'aaaaaaaaaaaaaaaaaaaaaaaaaafwI'
>>> "".join(next(new_iterator))
'aaaaaaaaaaaaaaaaaaaaaaaaaafwJ'
>>> new_iterator.__setstate__(state) # bonus: rewind iterator!
>>> "".join(next(new_iterator))
'aaaaaaaaaaaaaaaaaaaaaaaaaafwI'
To understand why the combination 10,001 corresponds to some tuple of length 29 like (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 22, 33), I invite you to browse the CPython sources to see how combinations objects allow themselves to be statefully copied and also the relevant section of the pickle docs.
If you don't require security against erroneous or maliciously constructed data, then you might consider to simply pickle such iterators to file rather than hook into the __setstate__ method manually.

adding tuples to a list in an if loop (python)

I'm working in python with symbulate for a probability course and running some simulations.
Setup: Two teams, A and B, are playing in a “best of n” game championship series, where n is an odd number. For this example, n=7, and the probability team A wins any individual game is 0.55. Approximate the probability that Team A wins the series, given that they win the first game.
Here is what I've got so far, which I think is along the right lines:
model = BoxModel([1, 0], probs=[0.55, .45], size=7, replace=True)
test = model.sim(10000)
for x in range(0,10000):
test1 = test[x]
if test1[0] == 1:
print (test1)
test1
The last two lines are where I'm having my difficulty. This 'for' and 'if' combination makes it so only the inputs that start with a '1' (i.e. Team A winning the first game) are displayed. I need to save these inputs into a table so that I can run some further testing on it.
How do I input the value of test1 into a table while those loops are running? Currently, test1 only outputs the x=10,000th value.
Edit: The "test" yields a list, 0-10000, of all the possible game outcomes. I need a list that only has the game outcomes that start with a "1".
Edit2: Output of "test" (before I run a "for" or "if") look like:
Index Result
0 (1, 1, 1, 0, 0, 1, 1)
1 (0, 1, 0, 1, 1, 0, 0)
2 (1, 1, 1, 1, 0, 1, 0)
3 (0, 0, 1, 1, 1, 1, 1)
4 (0, 0, 0, 0, 0, 0, 0)
5 (1, 1, 0, 1, 0, 0, 1)
6 (0, 0, 1, 0, 1, 1, 1)
7 (0, 1, 0, 0, 0, 0, 1)
8 (1, 1, 0, 1, 0, 1, 0)
... ...
9999 (1, 1, 0, 1, 0, 0, 0)
I need a "test' (or another variable) to contain something that looks EXACTLY like that, but only contains lines that start with "1".
So you're looking to store the results of each test? Why not store them in a list?
test1_results = []
for x in range(0,10000):
test1 = test[x]
# check if first element in sequence of game outcomes is a win for team A
if test1[0] == 1: # or '1' if you're expecting string
test1_results.append(test1)
You can the run print(test1_results) to print the entire list of results, but if you want to print the first n results, do print(test1_results[:n]).
If you want your if statement in there, you will have to slightly adjust the placement. What does your test object look like? Could you give us a small sample?
edit: updated if statement to reflect comment below
Based on your comment:
results_that_start_with_one = []
for result in test:
result_string = str(result)
if result_string[0] == "1":
results_that_start_with_one.append(result_string)
This iterates through each of your results in the list "test". It convert each to a string (i'm assuming they are some numeric value). It then takes the first character in the string, and asks if its a 1.

Python: appending to a base class list/tuple member

I was asked this question a moment ago and couldn't think of a pythonic solution so I thought I'd throw it out here for a better viewpoint.
What is the best way to extend a base class variable tuple/list etc in a super class?
The following works...
class BaseClass(object):
some_class_member = (1, 2, 3, 4, 5,)
class AnotherClass(BaseClass):
some_class_member = BaseClass.some_class_member + (6, 7,)
a = BaseClass()
a.some_class_member # (1, 2, 3, 4, 5)
b = AnotherClass()
b.some_class_member # (1, 2, 3, 4, 5, 6, 7)
But doesn't seem very pythonic as you have to reference the BaseClass by name so would have to update it if the name changed. Is there a better way to go about this?
I can kind of see your point, however, you're already referencing the BaseClass by name due to specifying it as a parent of your new subclass.
In the interest of the "what if the name changes" scenario, you could provide another layer of inheritance:
class MyBaseClass(BaseClass):
""" This is here so that all of my classes just inherits from this one """
class AnotherClass(MyBaseClass):
super_special_member = MyBaseClass.super_special_member + (6, 7, )
class ClassThatDoesntNeedSuperSpecialMember(MyBaseClass):
""" Cool. """
MyBaseClass would act as a sort of "constant" variable in that you can change what it inherits from and everything else will automatically update.
BaseClass in BaseClass.some_class_member does not mean super of AnotherClass. It just
BaseClass.some_class_member. You can access without instantiation.
>>> BaseClass.some_class_member
(1, 2, 3, 4, 5)
If your purpose is to access the value without instantiation, the simple answer is no.
However, to access the value after instantiation.
How about?
class AnotherClass(BaseClass):
def __init__(self):
self.some_class_member += (6, 7,)

Sorting chapters numbers, like 1.2.1 or 1.4.2.4

I have the Model Chapter:
class Chapter(ndb.Model):
tutKey = ndb.KeyProperty(kind='Tutorial')
title = ndb.StringProperty(required=True)
content = ndb.TextProperty(required=True)
note = ndb.TextProperty()
lvl = ndb.IntegerProperty(default=0)
order = ndb.IntegerProperty(default=0)
parentID = ndb.KeyProperty(kind='Chapter')
number = ndb.IntegerProperty()
'number' is the base chapter (chap1 or chap 1.2 have number = 1). 'lvl' is for the depth of the chapter, for example, in chap1.1.1 the lvl is 3 and in chap1.1.1.1 lvl is 4. And the 'order' says the order of the chap, for example, in chap1.2 'order' is 2 and in chap2.2 'order' is also 2.
How can i sort the following chapters (for example)?
chapter 2
chapter 2.1
chapter 1.1.1
chapter 1.1
chapter 1
I have been thinking... should i create a StringProperty to save the chapter number like "1.2.1" and then split the string by '.' ?
EDIT: i created a ndb.IntegerProperty(repeated=True) the equivalent to ListProperty suggested by JBernardo. I'm able to store the data correctly, but I can't get a way to sort by the property. Anyone knows how to do this?
You can represent your chapters by tuples with the length of the maximum depths of sub-chapters. Not used sub-chapters are represented by zeros. Then following would be a representation of your chapters:
chapter 2 (2, 0, 0)
chapter 2.1 (2, 1, 0)
chapter 1.1.1 (1, 1, 1)
chapter 1.1 (1, 1, 0)
chapter 1 (1, 0, 0)
If these chapters are added to a list l, this list can be sorted by the lists' sort method.
chapters = [(2, 0, 0), (2, 1, 0), (1, 1, 1), (1, 1, 0), (1, 0, 0)]
chapters.sort()
print chapters
This gives:
[(1, 0, 0), (1, 1, 0), (1, 1, 1), (2, 0, 0), (2, 1, 0)]
--- EDIT ---
In case of dynamic comparison you can add a function to the sort method via the key option. This function is applied to every element of the list before sorting. In the following expandChapter expands a chapter ch to the depth depth. The key argument in sort accepts only one parameter. Therefore, the maximum depth maxdepth is calculated and expandChapters is given to sort via anonymous lambda function.
def expandChapter(ch, depth):
ch = ch + (0,) * (depth - len(ch))
return ch
print expandChapter((1,), 3) # prints (1, 0, 0)
chapters = [(2,), (2, 1,), (1, 1, 1,), (1, 1), (1,)]
maxdepth = max([len(i) for i in chapters])
print maxdepth # prints 3
chapters.sort(key = lambda x: expandChapter(x, maxdepth))
print chapters
This gives the correct answer:
[(1,), (1, 1), (1, 1, 1), (2,), (2, 1)]
The first thing that sprung into my mind was to set up your routes to handle the chapters so to have something like:
1/1
1/2/1
etc
Found a simpler way to solve my problem.
I store the chapter in a different way. In ndb.IntegerProperty(repeated=True). This way i can sort it just my using:
newChaps = sorted(chaps, key=lambda obj: obj.version)
Where 'version' is the ndb.IntegerProperty.
Done.

Categories