Related
def mysum(L):
return 0 if not L else L[0] + mysum(L[1:])
def mysum(L):
return L[0] if len(L) == 1 else L[0] + mysum(L[1:])
def mysum(L):
first, *rest = L
return first if not rest else first + mysum(rest)
The latter two also work on a single string argument e.g mysum('spam') because strings are sequences of one-character strings.
The third variant works on arbitrary iterables, including open input files mysum(open(name)), but the others do not because they use index.
The function header def mysum(first *rest), although similar to the third variant, because it expects individual arguments not a single iterable.
The author seems to be implying that the variant with (first, *rest) as the input arguments wouldn't work with files but after experimenting with it, I found that it does work.
# Code I tried:
def mysum(first, *rest):
return first if not rest else first + mysum(*rest)
mysum(*open("script1.py")) works fine.
I think mysum(open("script1.py")) won't work because that what python would then see is first = open("script1.py and rest = [] which means it's gonna give me the <_io.TextIOWrapper name='script1.py' mode='r' encoding='cp1252'> because not [] is true.
The author wants a function that takes an iterable (e.g. a list, tuple, etc) as input and returns the sum, e.g. like this:
mysum(open("script1.py"))
When you write
mysum(*open("script1.py"))
This is roughly equivalent to
f = open("script1.py").readlines()
mysum(f[0], f[1], ..., f[n])
Note that here your code does not take an interable as input, instead it takes several separate arguments which is not what the author wanted.
Using a tuple to explain what happens. The *sequence syntax is used for unpacking.
numbers = (1, 2, 3)
mysum(*numbers) # this happens: mysum(1, 2, 3)
is equivalent to mysum(1, 2, 3). The members are taken from the iterable and fed into the function as arguments. Using *open('path/to/file') causes the file to be opened and its contents passed into mysum(L) as arguments. This is equivalent to mysum(open('path/to/file').read())
I am relatively new to python (and programming). I would like to understand what items, content, variables need to be passed as an explicit argument in the function definition and what can be used without being passed as an argument.
It seems that if a variable or item is introduced/defined in the module where the function is later located that it does not need to be passed as an argument.
Perhaps the below example from Codecademy will help to illustrate my question. Here we access the values from the created dictionaries stock and prices without passing either as an argument.
shopping_list = ["banana", "orange", "apple"]
stock = {
"banana": 6,
"apple": 0,
"orange": 32,
"pear": 15
}
prices = {
"banana": 4,
"apple": 2,
"orange": 1.5,
"pear": 3
}
def compute_bill(food):
total = 0
for item in food:
if stock[item] > 0:
total += prices[item]
stock[item] -= 1
return total
You can define a variable first, and then pass it to the function or loop just to initialize the variable ahead of time:
For instance:
i = 0
for i in some_list:
do something
i += 1
This will allow you to create i to 0 first and then loop through a list of an undetermined length and increment i to count over each item. If you were to try to run this function again however, it would do nothing as i has been increased and stays that way. You would need to reset i back to 0 or close out the interpreter and re-run the program.
Functions can do many different things. Say you want to say hello to someone:
def hello(name):
name = name
print(Hello, {}.format(name))
hello("Peter")
Output would be:
Hello, Peter
With this you can pass a variable without defining it, and it will store it inside of that function. If you try and call the "name" variable outside of the function though, you will get an error as its scope is only for that function. I hope this makes since and I highly recommend reading up more on Python Scope on their website here https://docs.python.org/3/tutorial/index.html This will tell you everything you need to know about functions in python.
Also as an aside, the reason the dictionaries are not passed as an argument in your example, is because we are using a For loop to iterate over the values. Look into For loops at the link I provided. Basically it is counting over each loop through and grabbing the prices of each item, and dynamically passing the location to each item via the "item" variable. once it reaches the end of the dictionary, it is giving you a total price, along with deducting inventory off the stock dictionary. So they are being used, but for a For loop in python there is no need to pass them as parameters. Now if you were to compare the two together, and your function was written in a way to take two dictionaries or strings as values to compare, then you would either need to define them in the function parameter itself, or create them ahead of time and pass the variable as a parameter.
Clarification:
So say you want to compare whats different in two dictionaries as a function. You can pass the dictionaries like so as parameters:
d1 = {'a': 912,'b':256,'c':350}
d2 = {'b':256,'x':290,'a':912}
def compare(a, b):
diff1 = set(a.items()) - set(b.items())
diff2 = set(b.items()) - set(a.items())
print(diff1)
print(diff2)
compare(d1,d2)
Output would be:
{('c', 350)}
{('x', 290)}
As you can see, we defined the dictionaries first, then passed them as arguments to the function to see what was different between the two.
You can even define the dictionaries on the fly:
compare({'a': 912, 'b': 256, 'c': 350}, {'b': 256, 'x': 290, 'a': 912})
Results remain the same.
You can also explicitly define they are used in the function directly but will limit the ability to use this function on any other dictionaries you wanted to compare.
d1 = {'a': 912,'b':256,'c':350}
d2 = {'b':256,'x':290,'a':912}
def compare():
diff1 = set(d1.items()) - set(d2.items())
diff2 = set(d2.items()) - set(d1.items())
print(diff1)
print(diff2)
compare()
Output remains the same as above. As you can see, we pass no arguments, and simply just compare the results and call that through:
compare()
It makes more sense though to pass arguments in this case, so you can compare multiple dictionaries anywhere in your program. Hope that clears that up some.
You can use python classes to initialize variables in the constructor and use them in the methods if you have many methods . So you can avoid passing same arguments to all functions
https://docs.python.org/3/tutorial/classes.html
In order to know how those list and dictionary is accessible in function defined later, you should know how python looks and resolves variables.
Look for local variable with same name if found use it
If no local variable is found then look for global variable if found then use it.
If global variable is not found then look for environment variable.(in sys.path) if found use that.
If variable doesn't exist then raise an error.
Looking for a variable is also like looking for a function . It is because of that you can also import python variables like importing a function. I hope you know this.
This question already has answers here:
Callable as the default argument to dict.get without it being called if the key exists
(6 answers)
Closed 6 years ago.
Seems like the fallback is called even if the key is present inside the dictionary. Is this an intended behaviour? How can workaround it?
>>> i = [1,2,3,4]
>>> c = {}
>>> c[0]= 0
>>> c.get(0, i.pop())
0
>>> c.get(0, i.pop())
0
>>> c.get(0, i.pop())
0
>>> c.get(0, i.pop())
0
>>> c.get(0, i.pop())
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: pop from empty list
When doing c.get(0, i.pop()), the i.pop() part gets evaluated before the result it returns is passed to the c.get(...). That's the reason that the error appears if a list i is empty due to the previous .pop() calls on it.
To get around this, you should either check if the list is not empty before trying to pop an element from it, or just try it an catch a possible exception:
if not i:
# do not call do i.pop(), handle the case in some way
default_val = i.pop()
or
try:
c.get(0, i.pop())
except IndexError:
# gracefully handle the case in some way, e.g. by exiting
default_val = i.pop()
The first approach is called LBYL ("look before you leap"), while the second is referred to as EAFP ("easier to ask for forgiveness than permission"). The latter is usually preferred in Python and considered more Pythonic, because the code does not get cluttered with a lot of safeguarding checks, although the LBYL approach has its merits, too, and can be just as readable (use-case dependent).
This is the expected results because you're directly invoking i.pop() which gets called before c.get().
The default argument to dict.get does indeed get evaluated before the dictionary checks if the key is present or not. In fact, it's evaluated before the get method is even called! Your get calls are equivalent to this:
default = i.pop() # this happens unconditionally
c.get(0, default)
default = i.pop() # this one too
c.get(0, default)
#...
If you want to specify a callable that will be used only to fill in missing dictionary values, you might want to use a collections.defaultdict. It takes a callable which is used exactly that way:
c = defaultdict(i.pop) # note, no () after pop
c[0] = 0
c[0] # use regular indexing syntax, won't pop anything
Note that unlike a get call, the value returned by the callable will actually be stored in the dictionary afterwards, which might be undesirable.
There is no real way to workaround this except using if...else... !
In your case, this code would work:
c[0] if 0 in c else i.pop()
This is intended behavior because i.pop() is an expression that is evaluated before c.get(...) is. Imagine what would happen if that weren't the case. You might have something like this:
def myfunction(number):
print("Starting work")
# Do long, complicated setup
# Do long, complicated thing with number
myfunction(int('kkk'))
When would you have int('kkk') to be evaluated? Would it be as soon as myfunction() uses it (after the parameters)? It would then finally have the ValueError after the long, complicated setup. If you were to say x = int('kkk'), when would you expect the ValueError? The right side is evaluated first, and the ValueError occurs immediately. x does not get defined.
There are a couple possible workarounds:
c.get(0) or i.pop()
That will probably work in most cases, but won't work if c.get(0) might return a Falsey value that is not None. A safer way is a little longer:
try:
result = c[0]
except IndexError:
result = i.pop()
Of course, we like EAFP (Easier to Ask Forgiveness than Permission), but you could ask permission:
c[0] if 0 in c else i.pop()
(Credits to #soon)
Both the arguments are evaluated before calling the get function. Thus in all calls the size of list is decreased by 1 even if the key is present.
Try something like
if c.has_key(0):
print c[0]
else:
print i.pop()
My function same_num takes values that are common to both sorted lists and appends them onto 'result'. It's using recursion and two offsets, pos1 and pos2 that are always initially set to 0, to compare values in the list. When running the function, it works fine the first time, however if I run the function a second time, the original result is appended with the answer I got from running it initially. Where am I going wrong?
result=[]
def same_num(list1,list2,pos1,pos2):
list1=sorted(list1)
list2=sorted(list2)
if pos1==len(list1) or pos2==len(list2):
return result
if list1[pos1]==list2[pos2]:
result.append(list1[pos1])
return same_num(list1,list2,pos1+1,pos2+1)
if list1[pos1]>list2[pos2]:
return same_num(list1,list2,pos1,pos2+1)
if list1[pos1]<list2[pos2]:
return same_num(list1,list2,pos1+1,pos2)
For example:
same_num([3,1,2,4],[3,1,2,4,5,6],0,0)=>[1,2,3,4]
Rerunning the previous example in the shell produces:
same_num([3,1,2,4],[3,1,2,4,5,6],0,0)=>[1, 2, 3, 4, 1, 2, 3, 4]
when it should still produce:
[1,2,3,4]
The problem is that result is a global variable. Globals are bad! You are adding stuff to result (result.append(...)) but never clearing it out after the first invocation of the same_num function.
(Although I can see why you are taking this approach, because conceptually it is often easier to approach recursive functions using global variables.)
If you make result a parameter of the same_num function that can be passed to recursive invocations of the same function... this issue is fixed.
def same_num(list1,list2,pos1,pos2,init_result=None):
# IMPORTANT: see remark below on why init_result=[]
# would not do what you expect
result = init_result if init_result is not None else []
list1=sorted(list1)
list2=sorted(list2)
if pos1==len(list1) or pos2==len(list2):
return result
if list1[pos1]==list2[pos2]:
result.append(list1[pos1])
return same_num(list1,list2,pos1+1,pos2+1,result)
if list1[pos1]>list2[pos2]:
return same_num(list1,list2,pos1,pos2+1,result)
if list1[pos1]<list2[pos2]:
return same_num(list1,list2,pos1+1,pos2,result)
# multiple invocations will return the same (expected) result
print( same_num([3,1,2,4],[3,1,2,4,5,6],0,0) )
print( same_num([3,1,2,4],[3,1,2,4,5,6],0,0) )
By the way, see "Common Python Gotchas: Mutable default arguments" for why I used init_result=None as the default, rather than init_result=[].
When running the function, it works fine the first time, however if I
run the function a second time, the original result is appended with
the answer I got from running it initially.
That is the exact issue. You are not emptying the previous result before you call it again. result still contains the values from the first time you ran the function.
For example, try running it like this instead:
output = same_num([3,1,2,4],[3,1,2,4,5,6],0,0)
print output
result = []
output = same_num([3,1,2,4],[3,1,2,4,5,6],0,0)
print output
Both outputs will be [1,2,3,4]
In Python, if I print different data types separated by commas, they will all act according to their __str__ (or possibly __repr__) methods, and print out a nice pretty string for me.
I have a bunch of variables like data1, data2... below, and I would love to get their total approximate size. I know that:
not all of the variables have a useful sys.getsizeof (I want to know the size stored, not the size of the container.) -Thanks to Martijn Pieters
the length of each of the printed variables is a good enough size estimate for my purposes
I'd like to avoid dealing with different data types individually. Is there any way to leverage a function like print to get the total length of data? I find it quite unlikely that something like this is not already built into Python.
>>> obj.data1 = [1, 2, 3, 4, 5]
>>> obj.data2 = {'a': 1, 'b':2, 'c':3}
>>> obj.data3 = u'have you seen my crossbow?'
>>> obj.data4 = 'trapped on the surface of a sphere'
>>> obj.data5 = 42
>>> obj.data6 = <fake a.b instance at 0x88888>
>>> print obj.data1, obj.data2, obj.data3, obj.data4, obj.data5, obj.data6
[1, 2, 3, 4, 5] {'a': 1, 'c': 3, 'b': 2} have you seen my crossbow? trapped on the surface of a sphere 42 meh
I'm looking for something like:
printlen(obj.data1, obj.data2, obj.data3, obj.data4, obj.data5, obj.data6)
109
I know most of you could write something like this, but I'm mostly asking if Python has any built-in way to do it. A great solution would show me a way to return the string that print prints in Python 2.7. (Something like print_r in PHP, which I otherwise feel is wholly inferior to Python.) I'm planning on doing this programmatically with many objects that have pre-filled variables, so no writing to a temporary file or anything like that.
Thanks!
As a side-note, this question arose from a need to calculate the approximate total size of the variables in a class that is being constructed from unknown data. If you have a way to get the total size of the non-callable items in the class (honestly, the total size would work too), that solution would be even better. I didn't make that my main question because it looks to me like Python doesn't support such a thing. If it does, hooray!
"A great solution would show me a way to return the string that print prints in Python 2.7."
This is roughly what print prints (possibly extra spaces, missing final newline):
def print_r(*args):
return " ".join((str(arg) for arg in args))
If you run in to lots of objects that aren't str-able use safer_str instead:
def safer_str(obj):
return str(obj) if hasattr(obj,"__str__") else repr(obj)
First of all, sys.getsizeof() is not the method to use to determine printed size. A python object memory footprint is a poor indicator for the number of characters required to represent a python object as a string.
You are looking for len() instead. Use a simple generator expression plus sum() to get a total:
def printlen(*args):
if not args:
return 0
return sum(len(str(arg)) for arg in args) + len(args) - 1
The comma between expressions tells print to print a space, so the total length print will write to stdout is the sum length of all string representations, plus the whitespace between the elements.
I am assuming you do not want to include the newline print writes as well.
Demo:
>>> printlen(data1, data2, data3, data4, data5, data6)
136
This should now do it correctly:
def printlen(*args):
return sum(map(len, map(str, args)))
For objects which do not support the str(obj) function. You could replace the str with a self made function or lambda:
def printlen(*args):
return sum(map(len, map(lambda x: str(x) if hasattr(x, '__str__') else '', args)))
If you want the length you can use this:
printlen = lambda *x: print(sum(len(str(i)) for i in x))
usage:
printlen(obj1, obj2, ..)
If you have an object structure and you want to know how much does it require to store it, you could also pickle/cpickle the object and use that number as a measure, and to also to store the data into database.