I have a list that I create inside of function1. I want to be able to access and modify it in function2. How can I do this without a global variable?
Neither function is nested within the other and I need to be able to generalize this for multiple lists in several functions.
I want to be able to access word_list and sentence_starter in other functions.
def Markov_begin(text):
print create_word_lists(text)
print pick_starting_point(word_list)
return starting_list
def create_word_lists(filename):
prefix_dict = {}
word_list = []
sub_list = []
word = ''
fin = open(filename)
for line in fin:
the_line = line.strip()
for i in line:
if i not in punctuation:
word+=(i)
if i in punctuation:
sub_list.append(word)
word_list.append(sub_list)
sub_list = []
word = ''
sub_list.append(word)
word_list.append(sub_list)
print 1
return word_list
def pick_starting_point(word_list):
sentence_starter = ['.','!','?']
starting_list = []
n = 0
for n in range(len(word_list)-1):
for i in word_list[n]:
for a in i:
if a in sentence_starter:
starting_list += word_list[n+1]
print 2
return starting_list
def create_prefix_dict(word_list,prefix_length):
while prefix > 0:
n = 0
while n < (len(word_list)-prefix):
key = str(''.join(word_list[n]))
if key in prefix_dict:
prefix_dict[key] += word_list[n+prefix]
else:
prefix_dict[key] = word_list[n+prefix]
n+=1
key = ''
prefix -=1
print Markov_begin('Reacher.txt')
You should refactor this as a class:
class MyWords(object):
def __init__(self):
self.word_list = ... #code to create word list
def pick_starting_point(self):
# do something with self.word_list
return ...
Usage
words = MyWords()
words.pick_starting_point()
...
You can simply use the list that first function creates as an argument of second function:
def some_list_function():
# this function would generate your list
return mylist
def some_other_function(mylist):
# this function takes a list as an argument and process it as you want
return result
some_other_function(some_list_function())
But if you need to use the list in multiple places (being processed by multiple functions) then storing it as a variable is not really a bad thing - even more, if your list generating function does some computing to generate the list, you're saving CPU by computing it only once.
If you do not want to a) use a global or b) return the list and pass it about, then you will have to use a class and hold your list in there.
The class route is best
Functions can have attribute values (For examples, see question 338101.)
In the current context, you could save and reference items like prefix_dict, word_list, sub_list, and word as individual attributes of whichever function computes them, as illustrated in the following example. However, use of a class, as suggested in other answers, is more likely to be understandable and maintainable in the long-term .
For example:
In [6]: def fun1(v):
fun1.li = range(v,8)
return v+1
...:
In [7]: def fun2(v):
fun2.li = range(v,12) + fun1.li
return v+2
...:
In [8]: fun1(3)
Out[8]: 4
In [9]: fun2(6)
Out[9]: 8
In [10]: fun2.li
Out[10]: [6, 7, 8, 9, 10, 11, 3, 4, 5, 6, 7]
Related
I have no idea what the difference is between these two codes. When I rum those codes in the scoring python code, it marks mine wrong. I would appreciate it if you can tell me the different between using a variable to make a new empty list and appending values verses just making a list with values in it. The first code is mine and the second code is the code from the answer sheet. Thank you very much :)
class Server:
def __init__(self):
self.list = []
def makeOrder(self,orderNum, orderList):
existance = False
for order in self.list:
if order[0]==orderNum:
existance = True
if existance == True:
return -1
else:
self.list.append([orderNum,orderList])
return [orderNum,orderList]
class Server:
def __init__(self):
self.q = []
# 1
def makeOrder(self, orderNumber, orderList):
existAlready = False
for order in self.q:
if order[0] == orderNumber:
existAlready = True
if existAlready == True:
return -1
else:
tmp = []
tmp.append(orderNumber)
tmp.append(orderList)
self.q.append(tmp)
return tmp
Functionally, these are both very similar. The only difference of substance I see (other than obvious variable names, etc) is in the return value.
In the first option, you can see that one list is appended to self.list, and a new (albeit identical in value) list is returned. This means that this is a different object.
self.list.append([orderNum,orderList])
return [orderNum,orderList]
However in the second option, you can clearly see that tmp is both pushed AND returned:
tmp = []
tmp.append(orderNumber)
tmp.append(orderList)
self.q.append(tmp)
return tmp
This is a single object that gets appended and returned.
Fundamentally, this mens that any modification to the returned list in the second option will be reflected inside self.q, while in the first option, the returned value is wholly independent. If you wanted to more or less replicate the behavior of option 1, you can change:
return tmp
with
return list(tmp)
Although keep in mind that if orderList is itself a mutable list, the same behavior will occur if you modify the returned value's [1] element. Since it is a reference to a data structure, modification of that list will also affect self.q (or self.list in the first option)
Example:
>>> class Foo:
... def __init__(self):
... self.q = []
... def bar(self, i, l):
... tmp = [i, l]
... self.q.append(tmp)
... return list(tmp)
...
>>> f = Foo()
>>> f.q
[]
>>> x = f.bar(1, [1,2,3])
>>> x
[1, [1, 2, 3]]
>>> x[1].append(4)
>>> x
[1, [1, 2, 3, 4]]
>>> f.q # it changed here as well
[[1, [1, 2, 3, 4]]]
Suppose I have arrays of tuples like so:
a = [('shape', 'rectangle'), ('fill', 'no'), ('size', 'huge')]
b = [('shape', 'rectangle'), ('fill', 'yes'), ('size', 'large')]
I am trying to turn these arrays into numerical vectors with each dimension representing a feature.
So the expected output we be something like:
amod = [1, 0, 1] # or [1, 1, 1]
bmod = [1, 1, 2] # or [1, 2, 2]
So the vector that gets created is dependent on what it has seen before (i.e rectangle is still coded as 1 but the new value 'large' gets coded as a next step up as 2).
I think I could use some combination of yield and a memoize function to help me with this. This is what I've tried so far:
def memoize(f):
memo = {}
def helper(x):
if x not in memo:
memo[x] = f(x)
return memo[x]
return helper
#memoize
def verbal_to_value(tup):
u = 1
if tup[0] == 'shape':
yield u
u += 1
if tup[0] == 'fill':
yield u
u += 1
if tup[0] == 'size':
yield u
u += 1
But I keep getting this error:
TypeError: 'NoneType' object is not callable
Is there a way I can create this function that has a memory of what it has seen? Bonus points if it could add keys dynamically so I don't have to hardcode things like 'shape' or 'fill'.
First off: this is my preferred implementation of the memoize
decorator, mostly because of speed ...
def memoize(f):
class memodict(dict):
__slots__ = ()
def __missing__(self, key):
self[key] = ret = f(key)
return ret
return memodict().__getitem__
except for some a few edge cases it has the same effect as yours:
def memoize(f):
memo = {}
def helper(x):
if x not in memo:
memo[x] = f(x)
#else:
# pass
return memo[x]
return helper
but is somewhat faster because the if x not in memo: happens in
native code instead of in python. To understand it you merely need
to know that under normal circumstances: to interpret adict[item]
python calls adict.__getitem__(key), if adict doesn't contain key,
__getitem__() calls adict.__missing__(key) so we can leverage the
python magic methods protocols for our gain...
#This the first idea I had how I would implement your
#verbal_to_value() using memoization:
from collections import defaultdict
work=defaultdict(set)
#memoize
def verbal_to_value(kv):
k, v = kv
aset = work[k] #work creates a new set, if not already created.
aset.add(v) #add value if not already added
return len(aset)
including the memoize decorator, that's 15 lines of code...
#test suite:
def vectorize(alist):
return [verbal_to_value(kv) for kv in alist]
a = [('shape', 'rectangle'), ('fill', 'no'), ('size', 'huge')]
b = [('shape', 'rectangle'), ('fill', 'yes'), ('size', 'large')]
print (vectorize(a)) #shows [1,1,1]
print (vectorize(b)) #shows [1,2,2]
defaultdict is a powerful object that has almost the same logic
as memoize: a standard dictionary in every way, except that when the
lookup fails, it runs the callback function to create the missing
value. In our case set()
Unfortunately this problem requires either access to the tupple that
is being used as the key, or to the dictionary state itself. With the
result that we cannot just write a simple function for .default_factory
But we can write a new object based on the memoize/defaultdict pattern:
#This how I would implement your verbal_to_value without
#memoization, though the worker class is so similar to #memoize,
#that it's easy to see why memoize is a good pattern to work from:
class sloter(dict):
__slots__ = ()
def __missing__(self,key):
self[key] = ret = len(self) + 1
#this + 1 bothers me, why can't these vectors be 0 based? ;)
return ret
from collections import defaultdict
work2 = defaultdict(sloter)
def verbal_to_value2(kv):
k, v = kv
return work2[k][v]
#~10 lines of code?
#test suite2:
def vectorize2(alist):
return [verbal_to_value2(kv) for kv in alist]
print (vectorize2(a)) #shows [1,1,1]
print (vectorize2(b)) #shows [1,2,2]
You might have seen something like sloter before, because it's
sometimes used for exactly this sort of situation. Converting member
names to numbers and back. Because of this, we have the advantage of
being able to reverse things like this:
def unvectorize2(a_vector, pattern=('shape','fill','size')):
reverser = [{v:k2 for k2,v in work2[k].items()} for k in pattern]
for index, vect in enumerate(a_vector):
yield pattern[index], reverser[index][vect]
print (list(unvectorize2(vectorize2(a))))
print (list(unvectorize2(vectorize2(b))))
But I saw those yields in your original post, and they've got me
thinking... what if there was a memoize / defaultdict like object
that could take a generator instead of a function and knew to just
advance the generator rather than calling it. Then I realized ...
that yes generators come with a callable called __next__() which
meant that we didn't need a new defaultdict implementation, just a
careful extraction of the correct member funtion...
def count(start=0): #same as: from itertools import count
while True:
yield start
start += 1
#so we could get the exact same behavior as above, (except faster)
#by saying:
sloter3=lambda :defaultdict(count(1).__next__)
#and then
work3 = defaultdict(sloter3)
#or just:
work3 = defaultdict(lambda :defaultdict(count(1).__next__))
#which yes, is a bit of a mindwarp if you've never needed to do that
#before.
#the outer defaultdict interprets the first item. Every time a new
#first item is received, the lambda is called, which creates a new
#count() generator (starting from 1), and passes it's .__next__ method
#to a new inner defaultdict.
def verbal_to_value3(kv):
k, v = kv
return work3[k][v]
#you *could* call that 8 lines of code, but we managed to use
#defaultdict twice, and didn't need to define it, so I wouldn't call
#it 'less complex' or anything.
#test suite3:
def vectorize3(alist):
return [verbal_to_value3(kv) for kv in alist]
print (vectorize3(a)) #shows [1,1,1]
print (vectorize3(b)) #shows [1,2,2]
#so yes, that can also work.
#and since the internal state in `work3` is stored in the exact same
#format, it be accessed the same way as `work2` to reconstruct input
#from output.
def unvectorize3(a_vector, pattern=('shape','fill','size')):
reverser = [{v:k2 for k2,v in work3[k].items()} for k in pattern]
for index, vect in enumerate(a_vector):
yield pattern[index], reverser[index][vect]
print (list(unvectorize3(vectorize3(a))))
print (list(unvectorize3(vectorize3(b))))
Final comments:
Each of these implementations suffer from storing state in a global
variable. Which I find anti-aesthetic but depending on what you're
planning to do with that vector later, that might be a feature. As I
demonstrated.
Edit:
Another day of meditating on this, and the sorts of situations where I might need it,
I think that I'd encapsulate this feature like this:
from collections import defaultdict
from itertools import count
class slotter4:
def __init__(self):
#keep track what order we expect to see keys
self.pattern = defaultdict(count(1).__next__)
#keep track of what values we've seen and what number we've assigned to mean them.
self.work = defaultdict(lambda :defaultdict(count(1).__next__))
def slot(self, kv, i=False):
"""used to be named verbal_to_value"""
k, v = kv
if i and i != self.pattern[k]:# keep track of order we saw initial keys
raise ValueError("Input fields out of order")
#in theory we could ignore this error, and just know
#that we're going to default to the field order we saw
#first. Or we could just not keep track, which might be
#required, if our code runs to slow, but then we cannot
#make pattern optional in .unvectorize()
return self.work[k][v]
def vectorize(self, alist):
return [self.slot(kv, i) for i, kv in enumerate(alist,1)]
#if we're not keeping track of field pattern, we could do this instead
#return [self.work[k][v] for k, v in alist]
def unvectorize(self, a_vector, pattern=None):
if pattern is None:
pattern = [k for k,v in sorted(self.pattern.items(), key=lambda a:a[1])]
reverser = [{v:k2 for k2,v in work3[k].items()} for k in pattern]
return [(pattern[index], reverser[index][vect])
for index, vect in enumerate(a_vector)]
#test suite4:
s = slotter4()
if __name__=='__main__':
Av = s.vectorize(a)
Bv = s.vectorize(b)
print (Av) #shows [1,1,1]
print (Bv) #shows [1,2,2]
print (s.unvectorize(Av))#shows a
print (s.unvectorize(Bv))#shows b
else:
#run the test silently, and only complain if something has broken
assert s.unvectorize(s.vectorize(a))==a
assert s.unvectorize(s.vectorize(b))==b
Good luck out there!
Not the best approach, but may help you to figure out a better solution
class Shape:
counter = {}
def to_tuple(self, tuples):
self.tuples = tuples
self._add()
l = []
for i,v in self.tuples:
l.append(self.counter[i][v])
return l
def _add(self):
for i,v in self.tuples:
if i in self.counter.keys():
if v not in self.counter[i]:
self.counter[i][v] = max(self.counter[i].values()) +1
else:
self.counter[i] = {v: 0}
a = [('shape', 'rectangle'), ('fill', 'no'), ('size', 'huge')]
b = [('shape', 'rectangle'), ('fill', 'yes'), ('size', 'large')]
s = Shape()
s.to_tuple(a)
s.to_tuple(b)
For example there is a list called Demo_list.
Demo_list = [4,5,6,7]
If i give
Demo_list[0]
we will get value as 4.
But if i gave only Demo_list[0] i want to get square of that value and the list should not be modified.
Is it possible?
Yes, it is possible.
variable = Demo_list[0]**2
The code above won't modify the list.
demo_list = [4, 6, 7, 8]
for i in range (len(demo_list)):
j = demo_list[i] * demo_list[i]
print j
May be you are looking something like that..
#For complete list
SqrtList = [x**2 for x in Demo_list]
#For single element
Sqrtvariable = Demo_list**2
You can use the < math > function
import math
print ( math.pow(demo[0],2)
where, 2 is the power that you want to raise the value in demo[0].
Edit (Inheriting from the collections, and overriding the abstract list methods , in your case (getitem),that you wish to modify).
import collections
class MyList(collections.MutableSequence):
def __init__(self, *args):
self.list=list()
self.extend(list(args))
def __len__(self):
return len(self.list)
def __getitem__(self,i):
return (self.list[i]**2)
def __delitem__(self,i):
del self.list[i]
def __setitem__(self,i,v):
self.list[i]=v
def insert(self,i,v):
self.list.insert(i,v)
def __str__(self):
return str(self.list)
Note: When you override these abstract methods, you need to define your list, with the type, you declared in this class. i.e.,
demo_list=MyList(1,2,3,4)
demo_list[1]
Output : 4
In this code, the generate() function is called implicitly by another function.
The question is as follows, in the code below is there a way to ensure that when the generate() function is called for instance 4 times in this case, values of b is saved in list p without replacing the preceding element appended to it, currently only one value of b is appended to t.
import random as rand
f = [1,2,3,4]
k = rand.choice(f)
h = rand.choice(f)
def generate():
global b # declare b as global
t = []
b = k + h
t.append(b) #Append b to all so that
print 'all:',t
c = 4**2
return c
generate()
def evaluate():
fit = (generate() * b) # generate() is used here
print "fit:", fit
# Some other expressions using variable b
p = []
for i in range(4):
p.append(b)
print 'p:',p
return fit
evaluate()
#output
all: [3]
fit: 48
p: [3, 3, 3, 3]
I think you have a scope problem. Read up on scoping in Python with the links below and consider this example:
>>> results = []
>>> i = [0]
>>> def test():
... i[0] = random.randint(0, 100)
... print i
... results.append(i)
...
>>> test()
[20]
>>> test()
[99]
>>> test()
[18]
>>> results
[[18], [18], [18]]
Note that even though the value of i[0] changes for each call to test(), we are appending the same list i to results each time - so when an element in i changes, the changes are reflected throughout the results list.
Links:
Short Description of the Scoping Rules?
https://www.inkling.com/read/learning-python-mark-lutz-4th/chapter-17/python-scope-basics
EDIT
To fix the above problem, you need to make sure you're not overwriting the same list in each call to test(). You can do this by creating a new list each time, or by copying the list before modifying it
>>> import copy
>>> def test():
... j = copy.copy(i)
... j[0] = random.randint(0, 100)
... print j
... results.append(j)
...
>>> results = []
>>> test()
[75]
>>> test()
[13]
>>> test()
[17]
>>> results
[[75], [13], [17]]
You mention that you are dealing with nested lists, in which case you should use copy.deepcopy instead of copy.copy. Deep copy will make copies of all of the elements in the list as well as the list itself.
t would need to be global ...
of coarse it only has one element you initialize it as empty and then append one item to it ... also its only available within the generate function ....
its also very unclear what you are trying to accomplish with generate (typically it would return a new individual...)
you can fix it so that t gets all the b's like so ...
t = []
def generate():
global b # declare b as global
b = k + h
t.append(b) #Append b to all so that
print 'all:',t
c = 4**2
return c
although I suspect you have alot more problems than that
I have a list of class objects and I'm using itertools.cycle() to continually loop through it.
class Letter:
def __init__(self,name):
self.name = name
self.att = 0
self.att1 = 0
Now I create the list of class objects
letterList = [Letter('a'),Letter('b'),Letter('c'),Letter('d')]
Then I make a sequence with itertools.cycle() like so:
seqList = itertools.cycle(letterList)
Anytime I want to go to the next item in the sequence and assign a value to one of the items attributes:
next(seqList).att = 1
But what if I want to assign another value to a different attribute? Is there another iterator method that will allow me to call the current item in the sequence like:
thisItem(seqList).att1 = 7
You can assign next() to a variable and access that variable.
this_one = next(seqList)
this_one.attr = value
If you just plan to infinitely loop over the same items, you can use a for loop (a break step is useful for getting out eventually though!):
for item in itertools.cycle(seqList):
item.attr1 = value1
item.attr2 = value2
if condition_met:
break
In most practical situations you simply use a variable, but just for fun, let's create a wrapper that would "remember" the last item (or N last items) for the given iterable:
from collections import deque
class recorded:
def __init__(self, it, size=1):
self.it = it
self.buf = deque(maxlen=size)
def __iter__(self):
return self
def next(self):
self.buf.append(next(self.it))
return self.buf[-1]
def last(self, n=0):
return self.buf[-1-n]
Now you can write:
it = recorded(itertools.cycle(letterList))
it.next()
it.last().something = 11
it.last().somethingElse = 111
or even:
it = recorded(itertools.cycle(letterList), 2)
it.next()
it.last().something = 11
it.next()
it.last(1).something = penultimate
The solution I found was:
next(seqList).att = 1
next(seqList)
next(seqList)
next(seqList)
next(seqList).att1 = 7
It's not pretty or elegant and it won't work very well for a larger set but it works for this situation and is very simple.