Functions¶
A function is an organizational structure that lets you group code into a reusable package. Not only do functions allow you to reuse the same code (possible with different inputs) over and over, they can be useful to help organize your code into logically related fragments, which can make your code easier to develop and understand.
Anatomy of a Function¶
A function definition looks like:
def my_function(inputs...):
...
return expression
Let’s break it down.
Function Name¶
In the example above, the function created is called
my_function. This has the effect of creating a variable named my_function, who’s
value is the function itself. In order to use my_function, you call it with the
following syntax:
my_function(...)
Where ... in this case is the inputs that you pass to your function.
Note
Because a function’s name becomes a variable, it is conventional for function names to be lowercase, with words separated by underscores. It follows all variable name requirements.
When working with Python in Grasshopper and Rhino 8, it’s not uncommon to need to work with functions that are PascalCased, where the first letter of each word is capitalized (including the first), instead of using underscores. This is a result of the Rhino/Grasshopper-specific functions being created by C# instead of Python.
Function Inputs¶
The number of inputs a function takes is determined by specifying comma-separated variable
names inside the parentheses immediately following def my_function, inputs...
in the example above. A function can accept as many inputs
as you’d like.
When calling the function, you need to pass in one argument for each input declared in the function definition:
def function0():
...
# would need to be called like
return_value = function0()
def function1(arg1):
...
# would need to be called like
return_value = function1(True)
def function2(arg1, arg2):
...
# would need to be called like
return_value = function2(1, "fizz")
The type of arguments used and the values passed in depend on the function and how it is being used.
Advanced Parameter Shenanigans
During this class, you may come across some parameter lists that aren’t just comma-separated variable names, and you might see some arguments in a function call that look like an assignment.
To understand these, we can look to how Python handles parameters getting passed in.
When you provide parameters to a function as shown above, they’re treated as
positional arguments. That is, the first value you supply when calling the function
is assigned to the first input parameter, the second to the second, etc. Alternatively,
you can pass variables by keyword, in which you explicitly say which input variable
to bind to which value. Using the function2 from above:
return_value = function2(arg2="fizz", arg1=1)
Important
All positional arguments must be passed in before all keyword arguments.
return_value = function2(arg2="fizz", 1)
is invalid.
Furthermore, attempting to pass an argument to a parameter name that doesn’t exist in the function definition will result in an error.
Additionally, functions in Python can be variadic, which means they can be made to
accept any number of arguments you supply to them. To do this, create a parameter,
and have it’s name start with an * (the common choice is *args). When called,
you can pass in as many comma-separated values as you’d like, and any values in excess
of the number of positional input parameters in the function definition will fill
a list that gets stored in the args variable. Keyword arguments can also
be made to be variadic, starting the variable name with ** (the common choice
for variable name is **kwargs). Any keyword arguments that do not match an
explicitly defined input parameter will populate a dict that gets stored in
the kwargs variable. An example function definition with both variadic positional
and keyword arguments:
def my_function(*args, **kwargs):
print(args)
print(kwargs)
my_function(1, 2, a=3, b=4)
# Console output:
# [1, 2]
# {'a': 3, 'b': 4}
Note
We will learn about lists and dicts in the next in-person class, during week 2.
Another trick you can apply to a function definition is to make certain parameters either positional-only or keyword-only. Positional-only parameters must be supplied positionally (an error will be raised if they are specified by name), and keyword-only parameters must be supplied by name.
By creating a positional-variadic function, any parameters listed after *args
will be keyword only. If you don’t want the function to be positional-variadic but
still have keyword-only parameters, make a parameter that is simply *. Extra
positional arguments will raise an error, because there’s no variable to save them
to, but any arguments listed after the * will be keyword-only. To make
positional-only arguments, place a / in the function definition following the
arguments you want to be positional-only. An example, non-variadic function definition:
def my_function(pos_only1, pos_only2, /, either_or1, either_or2, *, kw_only1, kw_only2):
...
Finally, you can supply default values to function parameters. If these parameters do not receive a value when the function is called, they are assigned the default value provided in the function definition. For example:
def my_function(a, b, c=2):
print(a, b, c)
my_function(0, 1)
# Console output:
# 0 1 2
my_function(1, 2, 3)
# Console output:
# 1 2 3
Important
All parameters with defaults must be declared at the end of the parameter list. This does mean you have to be mindful if you want to have positional-only or keyword-only arguments with defaults.
Function Output¶
A function in Python will always return some kind of value. This value is determined with
a return statement. As seen in the example above, return
is followed by an expression that sets the output value. Once return is called, the
function is immediately left, and no more code in the function body is executed.
You can have multiple return statements in one function, but only the first one called will get executed. You can use control statements to make multiple return statements meaningful:
def is_odd(number):
if number % 2 == 0:
return False
else:
return True
Note
The example above is a little contrived, because it can be reduced to:
def is_odd(number):
return number % 2 == 1
But it gets the point across.
Some special cases for function outputs exist:
If a
returnis not followed by an expression, the return value is set toNone.If a
returnstatement is never called by the time the end of the function has been reached, it will automatically returnNone.
Useful Functions¶
The following is a collection of helpful functions that you will probably find useful during this course (copied from the dedicated page).
This is a living list that will expand over the course of the semester.
print¶
The print() function allows you to output text to the console
while a program is running.
In a Grasshopper script node, instead of printing to the console, any printed text
gets sent to the out pin, which can be viewed with a Panel.
- print(*args, sep=' ', end='\n', file=None, flush=False)
Prints the values to a stream, or to sys.stdout by default.
- sep
string inserted between values, default a space.
- end
string appended after the last value, default a newline.
- file
a file-like object (stream); defaults to the current sys.stdout.
- flush
whether to forcibly flush the stream.
Note how print is variadic. You can supply as many arguments as you want to it,
and each argument will be converted to a string using that object’s __str__()
method. The stringified arguments will then be sent to the output stream, separated
by the sep argument, which is keyword-only. In the case of a Grasshopper script node,
the default output stream is captured into the out pin.
len¶
The len() function returns the size of a collection, or any
object that has defined the __len__ method.
- len(obj, /)
Return the number of items in a container.
max¶
The max() function accepts items that support
“rich” comparison using <, <=, ==, >=, and !=. Multiple versions
can be used:
- max(iterable, *[, default=obj, key=func]) value
- max(arg1, arg2, *args, *[, key=func]) value
With a single iterable argument, return its biggest item. The default keyword-only argument specifies an object to return if the provided iterable is empty. With two or more arguments, return the largest argument.
The second version is variadic and can be provided with as many positional inputs as you’d like. If, instead, an iterable is provided, all items within that iterable must be comparable with the rich comparison operators.
The keyword-only parameter key accepts a function that takes in a single input
and returns a value that is richly comparable. With a key, the output of the function
is instead the item, x, from the input that has the maximum key(x). This can
be used, for example, to compute an “argmax”, the index of the maximal item:
def collection_at_index(idx):
return collection[idx]
argmax = max(range(len(collection)), key=collection_at_index)
Note how collection_at_index is passed into key without using parentheses
to call it. This supplies the function itself to key instead of an output from
the function.
min¶
The min() function accepts items that support
“rich” comparison using <, <=, ==, >=, and !=. Multiple versions
can be used:
- min(iterable, *[, default=obj, key=func]) value
- min(arg1, arg2, *args, *[, key=func]) value
With a single iterable argument, return its smallest item. The default keyword-only argument specifies an object to return if the provided iterable is empty. With two or more arguments, return the smallest argument.
The second version is variadic and can be provided with as many positional inputs as you’d like. If, instead, an iterable is provided, all items within that iterable must be comparable with the rich comparison operators.
The keyword-only parameter key accepts a function that takes in a single input
and returns a value that is richly comparable. With a key, the output of the function
is instead the item, x, from the input that has the minimum key(x). This can
be used, for example, to compute an “argmax”, the index of the minimal item:
def collection_at_index(idx):
return collection[idx]
argmax = min(range(len(collection)), key=collection_at_index)
Note how collection_at_index is passed into key without using parentheses
to call it. This supplies the function itself to key instead of an output from
the function.
sorted¶
- sorted(iterable, /, *, key=None, reverse=False)
Return a new list containing all items from the iterable in ascending order.
A custom key function can be supplied to customize the sort order, and the reverse flag can be set to request the result in descending order.
abs¶
- abs(x, /)
Return the absolute value of the argument.
round¶
- round(number, ndigits=None)
Round a number to a given precision in decimal digits.
The return value is an integer if ndigits is omitted or None. Otherwise the return value has the same type as the number. ndigits may be negative.
reversed¶
The reversed() function can be used to reverse a sequence,
or any object that has defined the __reversed__ method.
- reversed(sequence, /)
Return a reverse iterator over the values of the given sequence.
Warning
reversed() does not return a sequence. It returns something
that can be iterated over with a for loop, but it cannot be indexed. If you need
a reversed version of your sequence that can be indexed, use a slice, as shown in
the Slicing Examples.
range¶
- range(stop) range object
- range(start, stop[, step]) range object
Return an object that produces a sequence of integers from start (inclusive) to stop (exclusive) by step. range(i, j) produces i, i+1, i+2, …, j-1. start defaults to 0, and stop is omitted! range(4) produces 0, 1, 2, 3. These are exactly the valid indices for a list of 4 elements. When step is given, it specifies the increment (or decrement).
Useful in for loops.
zip¶
- zip(*iterables, strict=False) zip object
>>> list(zip('abcdefg', range(3), range(4))) [('a', 0, 0), ('b', 1, 1), ('c', 2, 2)]
The zip object yields n-length tuples, where n is the number of iterables passed as positional arguments to zip(). The i-th element in every tuple comes from the i-th iterable argument to zip(). This continues until the shortest argument is exhausted.
If strict is true and one of the arguments is exhausted before the others, raise a ValueError.
Useful in for loops in conjunction with tuple unpacking:
for item1, item2 in zip(collection1, collection2):
...
enumerate¶
- enumerate(iterable, start=0)
Return an enumerate object.
- iterable
an object supporting iteration
The enumerate object yields pairs containing a count (from start, which defaults to zero) and a value yielded by the iterable argument.
- enumerate is useful for obtaining an indexed list:
(0, seq[0]), (1, seq[1]), (2, seq[2]), …
Useful in for loops in conjunction with tuple unpacking:
for idx, item in enumerate(collection):
...
Type Conversions¶
Many built-in types can be converted to one another by passing them to the new type’s
constructor. For example, to convert something to a string, use the
str(). To convert something to an integer, use the
int(). If the conversion fails, an error will be
raised.
- class str(object='')
- class str(bytes_or_buffer[, encoding[, errors]]) str
Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.__str__() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to ‘strict’.
- class int([x])
- class int(x, base=10) integer
Convert a number or string to an integer, or return 0 if no arguments are given. If x is a number, return x.__int__(). For floating-point numbers, this truncates towards zero.
If x is not a number or if base is given, then x must be a string, bytes, or bytearray instance representing an integer literal in the given base. The literal can be preceded by ‘+’ or ‘-’ and be surrounded by whitespace. The base defaults to 10. Valid bases are 0 and 2-36. Base 0 means to interpret the base from the string as an integer literal. >>> int(‘0b100’, base=0) 4
- class float(x=0, /)
Convert a string or number to a floating-point number, if possible.
- class bool(x)
Returns True when the argument x is true, False otherwise. The builtins True and False are the only two instances of the class bool. The class bool is a subclass of the class int, and cannot be subclassed.