From the doc of random.shuffle(x[, random]), it says:
The optional argument random is a 0-argument function returning a random float in [0.0, 1.0); by default, this is the function random()
Could someone please explain what 0-argument function means and give an example of random.shuffle() with the optional argument random? I searched but couldn't find any example for that case. Also, what did it mean by "this is the function random()"? Does this refer to the optional argument?
It means you can pass in the name of a function which does not require an argument.
def foo():
return 0.5
is such a function.
def bar(limit):
return limit
is not, because it requires an argument limit.
Usage example:
random.shuffle(itemlist, random=foo)
If the input itemlist was [1, 2, 3] it will now be [1, 3, 2]. I have established this experimentally, and I suppose how exactly the shuffle operation uses the output from the random function could change between Python versions.
The default if you don't specify anything is the function random().
One possible use case for this is if you want predictable output e.g. for a test case. Another is if you want nonuniform distribution - for example, a random function which prefers small values over large ones, or implements e.g. Poisson or normal distribution.
A 0-argument function has an empty argument list.
def random_seed(): # Zero arguments here.
return MY_CONFIG.predictble_random_seed # Some imaginary config.
random.shuffle(some_list, random_seed) # Always the same shuffling.
The point of this arrangement is to allow to control the predictability of the shuffling. You can return a really random number (from timer, /dev/urandom, etc, as random.random() does) in production, and a controlled number in a test environment:
def get_random_generator(environment):
if environment == 'test':
return lambda: 0.5 # A 0-argument callable returning a constant.
else:
return random.random # A function returning a random number.
# ...
# The below is predictable when testing in isolation,
# unpredictable when running in production.
# We suppose that `environment` has values like 'test' and 'prod'.
random.shuffle(entries, get_random_generator(environment))
The random parameter is the seed. Then if you always use the same seed, it always reorder your array with the same logic. See the exemple. 5 is at index 4 and go to 0. 6 go to 4 (Old index of 5) then if we reuse the same seed, 6 go the index 0 because 6 is at index 4 like 5 at the first shuffle
Exemple:
>>> import random
>>> r = random.random()
>>> r
0.4309619702601998
>>> x = [1, 2, 3, 4, 5, 6]
>>> random.shuffle(x, lambda: r)
>>> x
[5, 1, 4, 2, 6, 3]
>>> random.shuffle(x, lambda: r)
>>> x
[6, 5, 2, 1, 3, 4]
>>> x = [1, 2, 3, 4, 5, 6]
>>> random.shuffle(x, lambda: r)
>>> x
[5, 1, 4, 2, 6, 3]
Source
Related
When calling the function below, I can provide values that will be used instead of the default parameters in the function (see below).
cerebro.addstrategy(GoldenCross, fast=10, slow=25)
This works great for a small number of known parameters, but I am moving up to more complex systems. Essentially, I need to pass a fast_1, fast_2, fast_3, etc.... The total amount of these parameters will change (always around 100, but it can vary). Is there a statement that I can write that will dynamically add X amount of parameters to my function call?
I have tried using a for statement in the function call, but I received a syntax error.
I understood your question of two ways:
You want to call your function passing to it different parameters (that are optional), you can accomplish it like this:
def add(first, second=0, third=3):
return (first+second+third)
number_list = list(range(1, 200)) # Generates a list of numbers
result = [] # Here will be stored the results
for number in number_list:
# For every number inside number_list the function add will
# be called, sending the corresponding number from the list.
returned_result = add(1,second=number)
result.insert(int(len(result)), returned_result)
print(result) # Can check the result printing it
You want your function handles any number of optional parameters, as you don't know any way to determine how many they are, you can send a list or parameters, like this:
def add(first,*argv):
for number in argv:
first += number
return first
number_list = (list(range(1, 200))) # Generates a list of numbers
result = add(1,*number_list) # Store the result
print(result) # Can check the result printing it
Here you can find more information about *args
how about using *?
def addstrategy(GoldenCross, *fast, slow = 25): can be an example.
>>> def foo(a, *b, c = 36):
print(a, b, c)
>>> foo(1, 2, 3, 4, 5)
1 (2, 3, 4, 5) 36
You need to initialize fast in this case, however.
Two ways: Either use a variable number of arguments using * on the parameter or treat the parameter as an iterable.
def fun1(positional, optional="value", *args):
print(args) # args here is a tuple, since by default variable number of args using * will make that parameter a tuple.
def fun2(positional, args, optional="value"):
print(args) # args here will be dependant on the argument you passed.
fun1("some_value", "value", 1, 2, 3, 4, 5) # args = (1, 2, 3, 4, 5)
fun2("some_value", [1, 2, 3, 4, 5]) # args = [1, 2, 3, 4, 5]
I am learning Python3 and as I can see in the past, with Python2, it was possible to create a list of numbers with range() by just passing a single argument, which would be the last number of list + 1 in the list:
range(4) # == [0, 1, 2, 3]
and the start and step values were defaulted to 0 and 1.
But in Python3 for some reason, it is no longer possible to omit those 2 arguments and for the same result we would need to wrap the range() function with the list() function:
range(0, 4, 1) # == range(0, 4)
range(4) # == range(0, 4)
list(range(4)) # == [0, 1, 2, 3]
Question(s):
What is the reason behind that? Why was the functionality changed this way? Is there a good reason for that?
P.S. or maybe I am doing something incorrectly. Here, I am talking about range() function in general, whether it is being used in for-loops, just to create a list or for any other purpose
You've unfortunately been misled by the repr of range:
>>> range(4)
range(0, 4)
It is actually the same interface, but this is returning a lazily-generated sequence now, as opposed to a list. You may iterate a range instance to consume the values:
>>> list(range(4))
[0, 1, 2, 3]
See Python range() and zip() object type for more details about this change.
I am going through functions in Learning Python, this one is giving me a little trouble understanding, was wondering if anyone could help me understand it better, more specifically the if test(arg, res)
When I run the first function, it doesn't matter what I put as the first argument, then the second argument has to be a list, and it just spits the list back as written. I'm confused on when you combine the functions and print them together how it gives the max and min.
def minmax(test, *args):
res = args[0]
for arg in args[1:]:
if test(arg, res):
res = arg
return res
def lessthan(x, y): return x < y
def grtrthan(x, y): return x > y
print(minmax(lessthan, 4, 2, 1, 5, 6, 3))
print(minmax(grtrthan, 4, 2, 1, 5, 6, 3))
You seem to be misunderstanding how the starred function argument works. When you call your function as minmax(something, [1, 2, ...]), args becomes a tuple of one element equal to [1, 2, ...]. Therefore the body of the loop never gets executed and the value of test does not matter. The function is supposed to be invoked as minmax(something, 1, 2, ...). This way args gets the value (1, 2, ...) and the loop goes through its elements.
Clarification: The answer is based on what you wrote. Your code snippet is correct and works exactly as intended.
I haven't read that book so I don't know the context, but it looks that function (minmax) is not well named. It does not return two values, it only returns one -- and which one depends on the testing function you give it. If you pass in the lessthan function:
print(minmax(lessthan, 4, 2, 1, 5, 6, 3))
you'll get 1, and if you pass in the grtrthan function:
print(minmax(grtrthan, 4, 2, 1, 5, 6, 3))
you'll get 6.
I would like to ask what the following does in Python.
It was taken from http://danieljlewis.org/files/2010/06/Jenks.pdf
I have entered comments telling what I think is happening there.
# Seems to be a function that returns a float vector
# dataList seems to be a vector of flat.
# numClass seems to an int
def getJenksBreaks( dataList, numClass ):
# dataList seems to be a vector of float. "Sort" seems to sort it ascendingly
dataList.sort()
# create a 1-dimensional vector
mat1 = []
# "in range" seems to be something like "for i = 0 to len(dataList)+1)
for i in range(0,len(dataList)+1):
# create a 1-dimensional-vector?
temp = []
for j in range(0,numClass+1):
# append a zero to the vector?
temp.append(0)
# append the vector to a vector??
mat1.append(temp)
(...)
I am a little confused because in the pdf there are no explicit variable declarations. However I think and hope I could guess the variables.
Yes, the method append() adds elements to the end of the list. I think your interpretation of the code is correct.
But note the following:
x =[1,2,3,4]
x.append(5)
print(x)
[1, 2, 3, 4, 5]
while
x.append([6,7])
print(x)
[1, 2, 3, 4, 5, [6, 7]]
If you want something like
[1, 2, 3, 4, 5, 6, 7]
you may use extend()
x.extend([6,7])
print(x)
[1, 2, 3, 4, 5, 6, 7]
Python doesn't have explicit variable declarations. It's dynamically typed, variables are whatever type they get assigned to.
Your assessment of the code is pretty much correct.
One detail: The range function goes up to, but does not include, the last element. So the +1 in the second argument to range causes the last iterated value to be len(dataList) and numClass, respectively. This looks suspicious, because the range is zero-indexed, which means it will perform a total of len(dataList) + 1 iterations (which seems suspicious).
Presumably dataList.sort() modifies the original value of dataList, which is the traditional behavior of the .sort() method.
It is indeed appending the new vector to the initial one, if you look at the full source code there are several blocks that continue to concatenate more vectors to mat1.
append is a list function used to append a value at the end of the list
mat1 and temp together are creating a 2D array (eg = [[], [], []]) or matrix of (m x n)
where m = len(dataList)+1 and n = numClass
the resultant matrix is a zero martix as all its value is 0.
In Python, variables are implicitely declared. When you type this:
i = 1
i is set to a value of 1, which happens to be an integer. So we will talk of i as being an integer, although i is only a reference to an integer value. The consequence of that is that you don't need type declarations as in C++ or Java.
Your understanding is mostly correct, as for the comments. [] refers to a list. You can think of it as a linked-list (although its actual implementation is closer to std::vectors for instance).
As Python variables are only references to objects in general, lists are effectively lists of references, and can potentially hold any kind of values. This is valid Python:
# A vector of numbers
vect = [1.0, 2.0, 3.0, 4.0]
But this is perfectly valid code as well:
# The list of my objects:
list = [1, [2,"a"], True, 'foo', object()]
This list contains an integer, another list, a boolean... In Python, you usually rely on duck typing for your variable types, so this is not a problem.
Finally, one of the methods of list is sort, which sorts it in-place, as you correctly guessed, and the range function generates a range of numbers.
The syntax for x in L: ... iterates over the content of L (assuming it is iterable) and sets the variable x to each of the successive values in that context. For example:
>>> for x in ['a', 'b', 'c']:
... print x
a
b
c
Since range generates a range of numbers, this is effectively the idiomatic way to generate a for i = 0; i < N; i += 1 type of loop:
>>> for i in range(4): # range(4) == [0,1,2,3]
... print i
0
1
2
3
I have a Python function that takes a list as a parameter. If I set the parameter's default value to an empty list like this:
def func(items=[]):
print items
Pylint would tell me "Dangerous default value [] as argument". So I was wondering what is the best practice here?
Use None as a default value:
def func(items=None):
if items is None:
items = []
print items
The problem with a mutable default argument is that it will be shared between all invocations of the function -- see the "important warning" in the relevant section of the Python tutorial.
I just encountered this for the first time, and my immediate thought is "well, I don't want to mutate the list anyway, so what I really want is to default to an immutable list so Python will give me an error if I accidentally mutate it." An immutable list is just a tuple. So:
def func(items=()):
print items
Sure, if you pass it to something that really does want a list (eg isinstance(items, list)), then this'll get you in trouble. But that's a code smell anyway.
For mutable object as a default parameter in function- and method-declarations the problem is, that the evaluation and creation takes place at exactly the same moment. The python-parser reads the function-head and evaluates it at the same moment.
Most beginers asume that a new object is created at every call, but that's not correct! ONE object (in your example a list) is created at the moment of DECLARATION and not on demand when you are calling the method.
For imutable objects that's not a problem, because even if all calls share the same object, it's imutable and therefore it's properties remain the same.
As a convention you use the None object for defaults to indicate the use of a default initialization, which now can take place in the function-body, which naturally is evaluated at call-time.
In addition and also to better understand what python is, here my little themed snippet:
from functools import wraps
def defaultFactories(func):
'wraps function to use factories instead of values for defaults in call'
defaults = func.func_defaults
#wraps(func)
def wrapped(*args,**kwargs):
func.func_defaults = tuple(default() for default in defaults)
return func(*args,**kwargs)
return wrapped
def f1(n,b = []):
b.append(n)
if n == 1: return b
else: return f1(n-1) + b
#defaultFactories
def f2(n,b = list):
b.append(n)
if n == 1: return b
else: return f2(n-1) + b
>>> f1(6)
[6, 5, 4, 3, 2, 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, 3, 2, 1]
>>> f2(6)
[1, 2, 3, 4, 5, 6]