Python Language Functions: Difference between revisions

From NovaOrdis Knowledge Base
Jump to navigation Jump to search
 
(249 intermediate revisions by the same user not shown)
Line 1: Line 1:
=Internal=
=Internal=
* [[Python Language#Functions|Python Language]]
* [[Python Language#Functions|Python Language]]
* [[Java_Language#Method_Parameter|Java Method Parameters and Arguments]]
=TODO=
<font color=darkkhaki>
* TO PROCESS [[PyOOP]] "Extending built-in functions" + "Case study"
* TO PROCESS [[PyOOP]] "An alternative to method overloading" + "Default arguments" + "Variable argument list" + "Unpacking arguments"
* TO PROCESS [[PyOOP]] "Functions are objects too"
</font>
=Overview=
=Overview=
=Function Declaration=
A function is a [[#Function_Name_Rules|named]] or [[#Lambda|unnamed]] piece of code, which takes any number and type of input [[#Function_Parameters|parameters]] and returns any number and type of [[#Return_Value|output results]]. When a function returns more than one return value, it does that by packaging all the return values in a tuple. The function is first [[#Function_Definition|defined]], then [[#Function_Invocation|invoked]], or called. As pretty much everything else, functions are objects, and they are "first-class citizens", in that they can be assigned to variables, used as arguments, and return them from other functions.
<syntaxhighlight lang='python'>
def f():
  pass
 
print(type(f))
</syntaxhighlight>
will display:
<syntaxhighlight lang='text'>
<class 'function'>
</syntaxhighlight>
 
Each function define its own [[Python_Language#Variables_Namespace_and_Scope|namespace]]: variables defined inside a function are distinct from the variables with the same name defined in the main program.
=Check whether an Object Instance is a Function=
<syntaxhighlight lang='python'>
import types
 
def f():
    pass
 
assert isinstance(f, types.FunctionType)
</syntaxhighlight>
 
Another way:
<syntaxhighlight lang='python'>
def f():
    pass
 
assert callable(f)
</syntaxhighlight>
 
=Function Definition=
A function is defined by the reserved word <code>[[Python_Language#def|def]]</code>, followed by the function name, parentheses enclosing input parameters and a colon:
<syntaxhighlight lang='py'>
<syntaxhighlight lang='py'>
def function_name(parameters):
def function_name([parameters]):
   <function body>
   <function body>
</syntaxhighlight>
</syntaxhighlight>
Line 11: Line 51:
   <function body>
   <function body>
</syntaxhighlight>
</syntaxhighlight>
A '''parameter''' is a variable name used in the function definition. The parameters are handles for [[#Argument|arguments]] for a particular function invocation. Parameters are optional, a function may have no parameters.
Two blank lines are expected after a function definition.
==Return Value==
<syntaxhighlight lang='py'>
def add(a, b):
  c = a + b
  return c
 
 
print(add(1, 2))
</syntaxhighlight>
 
==Function Name Rules==
==Function Name Rules==
The function name rules for function names is the same as for [[Python_Language#Variable_Naming_Rules|variable names]].
The function name rules for function names is the same as for [[Python_Language#Variable_Naming_Rules|variable names]].
==<span id='Parameter'></span>Function Parameters==
A '''parameter''' is a variable name used in the function definition. Each parameter translates to a variable private to the function. The parameters are handles for [[#Argument|arguments]] for a particular function invocation.
A function can have '''positional''' parameters and '''keyword''' parameters. All positional arguments must be specified when calling the function. Keyword parameters are optional and are most commonly used to specify default values or optional parameters.
When invoked, the keyword arguments must follow the positional arguments, if any. Keyword arguments can be specified in any order, it does not need to match the order in which the corresponding keyword parameters have been declared.
Parameters are optional, a function may have no parameters. However, once a parameter is individually specified in the function definition, and that parameter does not have a [[#Default_Parameter_Values|default parameter value]], a corresponding [[#Positional_Argument|positional argument]] must be provided when the function is invoked, otherwise the invocation fails with:
<font size=-2>
TypeError: my_function() missing 1 required positional argument: 'arg1'
</font>
The function can be invoked with a variable number of arguments if the [[#Gather_Positional_Arguments_with_.2A|positional arguments are gathered with *]] or the [[#Gather_Keyword_Arguments_with_.2A.2A|keyword arguments are gathered with **]] in the function definition, or both.
Also see: {{Internal|Variables,_Parameters,_Arguments#Parameter|Variables, Parameters, Arguments}}
===Default Parameter Values===
Default values can be declared for function parameters. The declared default value is used if the caller does not provide the corresponding argument. Naturally, if an argument is provided, it is used instead of the default value.
<syntaxhighlight lang='python'>
def make_item(name, color='green', size=1):
    return f"made {color} {name} in size {size}"
assert make_item("hat", "red") == "made red hat in size 1"
</syntaxhighlight>


⚠️Default argument values are initialized when the function is [[#Function_Definition|defined]], not when it is invoked. The default argument values are part of the function state, which is initialized only once when it is defined. This behavior is exemplified in the following example:
<syntaxhighlight lang='python'>
def m(arg, result=[]):
    result.append(arg)
    print(result)
m(1) # will display [1]
m(2) # will display [1, 2], the state of the list is preserved between the invocation  and is not reset on invocation.
</syntaxhighlight>
===Function Parameter Type Hints===
{{External|https://docs.python.org/3/library/typing.html#module-typing}}
{{External|https://typing.readthedocs.io/en/latest/}}
Since 3.5, Python has support for type hints, which can be applied to both function parameters and [[#Return_Value_Type_Hints|return values]]. The Python runtime does not enforce function and variable type annotations. However, they can be used by third party tools such as type checkers, IDEs, linters, etc. This is a function whose parameter and return has type hints.
<syntaxhighlight lang='python'>
def some_func(arg: str) -> str:
  return "hello " + arg
def some_other_func(x: str | int) -> None:
  ...
</syntaxhighlight>
===Function Parameter Type Hinds and Default Values===
<syntaxhighlight lang='python'>
def some_func(par1: str = 'blue') -> str:
    return par1.upper()
assert some_func() == "BLUE"
</syntaxhighlight>
Spaces around '=' are expected. While the form without spaces is syntactically valid, it will be singled out as a weak warning "PEP 8: E252 missing whitespace around parameter equals".
<syntaxhighlight lang='python'>
def some_func(par1: str='blue') -> str: # PEP 8: E252 missing whitespace around parameter equals
    ...
</syntaxhighlight>
==Function Body==
The body needs to be indented. It includes statements, an optional [[#Docstring|docstring]] and an optional [[#Return|return statement]].
===Docstring===
{{External|https://www.python.org/dev/peps/pep-0257/}}
A docstring is a string declared at the beginning of the function body that serves as function documentation. Upon declaration, the string value is assigned to the <code>__doc__</code> internal variable within the function instance. The string can be a one-line or multi-line and declared between [[Python_Language#Quotes|single-quotes, double-quotes or triple quotes]]. The recommended style is to use [[Python_Language#Triple_Double-Quoted_Strings|triple double-quoted]] strings as docstrings. <font color=darkkhaki>TO PROCESS: https://www.python.org/dev/peps/pep-0257/</font>
<syntaxhighlight lang='python'>
def m(color='green'):
    """
    m() builds a widget of the given color.
    It gets the color and reports what it is going to build.
    I'll do so by:
        1. Reporting the color
        2. Building the widget
    Keyword arguments:
    color              -- the color (default green)
    size              -- the size (default 1)
   
    :return: the widget
    """
    print(f"I will build a {color} widget")
</syntaxhighlight>
<syntaxhighlight lang='python'>
def m(color='green'):
    """
    This is what the function does.
    :param color: this is the color
    :return: the widget
    """
    print(f"I will build a {color} widget")
</syntaxhighlight>
The function's docstring can be printed with the <code>[[#help.28.29|help()]]</code> function, by passing the function name as argument. The output will be formatted.
<syntaxhighlight lang='python'>
help(m)
</syntaxhighlight>
To print the raw docstring:
<syntaxhighlight lang='python'>
print(m.__doc__)
</syntaxhighlight>
===<span id='Return'></span>Return Value===
The return statement, which is introduced by the <code>return</code> [[Python_Language#Reserved_Words|reserved word]], ends the function execution and sends back the output result of the function.
<syntaxhighlight lang='py'>
def ...
  ...
  return optional_return_value
</syntaxhighlight>
The compiler will not prevent to declare other statements after the <code>return</code> statement, they be never executed, though.  If the function body does not contain a <code>return</code> statement, it will return <code>[[Python_Language#None|None]]</code>.
====Returning Multiple Values====
A function actually returns just '''one''' object, a tuple, which is then being [[Python_Language_Tuple#Tuple_Unpacking|unpacked]] into the result variables.
<syntaxhighlight lang='py'>
def shades(color):
  return 'dark ' + color, 'light ' + color, 'transparent ' + color
a, b, c = shades('red')
assert 'dark red' == a
assert 'light red' == b
assert 'transparent red' == c
</syntaxhighlight>
====Return Value Type Hints====
{{External|https://docs.python.org/3/library/typing.html#module-typing}}
Since 3.5, Python has support for type hints, which can be applied to both [[#Function_Parameter_Type_Hints|function parameters]] and return values:
<syntaxhighlight lang='py'>
def some_func(par1: str) -> str:
    return par1.upper()
def some_other_func(x: str) -> int | None:
  ...
</syntaxhighlight>
For more details on type hints, see [[#Function_Parameter_Type_Hints|Function Parameter Type Hints]] above.
==Style==
Exactly 2 blank lines are expected after a function definition. Also see: {{Internal|Python_Style#Overview|Python Style}}
=Function Invocation=
=Function Invocation=
<span id='Argument'></span>When the function is invoked, we pass an '''argument''' for each parameter declared in the function definition. An argument is a value that is passed into the function as function's input.  Arguments are passed in parentheses and they are separated by commas. If the function has no parameters, we pass no arguments, but the parentheses still need to be provided.
A function is invoked by prepending parantheses to the name of the function and specifying [[#Argument|arguments]] between parentheses. With no parentheses, Python treats the function like any other object.
==<span id='Argument'></span>Function Arguments==
When the function is invoked, we pass an '''argument''' for each parameter declared in the function definition. An argument is a value that is passed into the function as function's input. When the function is called with arguments, the values of those argument are copied to their corresponding [[#Parameter|parameters]] inside the function. The argument can be another variable, defined in the block that invokes the function.  Arguments are passed in parentheses and they are separated by commas. If the function has no parameters, we pass no arguments, but the parentheses still need to be provided.
<syntaxhighlight lang='py'>
<syntaxhighlight lang='py'>
function_name(arguments)
function_name(arguments)
Line 24: Line 206:
function_name(arg1, arg2, arg3)
function_name(arg1, arg2, arg3)
</syntaxhighlight>
</syntaxhighlight>
Python is unusually flexible in the manner it handles function arguments. The most common option is to specify the arguments [[#Positional_Arguments|positionally]]. Another option is to use [[#Keyword_Arguments|keyword arguments]]. Positional and keyword argument can be mixed, but in this case the positional arguments need to come first.
Also see: {{Internal|Variables,_Parameters,_Arguments#Argument|Variables, Parameters, Arguments}}
===<span id='Positional_Argument'></span>Positional Arguments===
When passing the arguments positionally, they map in order on the declared [[#Positional_Parameter|positional parameters]]. Although very common, a downside of positional arguments is that you need to remember the meaning of each position.
<syntaxhighlight lang='python'>
def menu(appetizer, entree, dessert):
    print(f"Appetizer: {appetizer}, entree: {entree}, dessert: {dessert}")
menu('salad', 'chicken', 'cake')
</syntaxhighlight>
Even if only positional parameters were declared in the function definition, the function can be invoked with keyword parameters in any order. For the above function declaration, the following invocation is valid:
<syntaxhighlight lang='python'>
menu(dessert='chocolate', entree='steak', appetizer='salad')
</syntaxhighlight>
===Gather Positional Arguments with *===
An asterisk (*) preceding a function parameter declaration indicates that a variable number of positional arguments the function is invoked with are grouped together into a [[Python_Language_Tuple#Overview|tuple]] of parameter values. It is a common idiom in Python to call to call the tuple parameter <code>args</code>:
<syntaxhighlight lang='python'>
def some_func(*args):
  print('tuple:', args)
some_func() # will display "tuple: ()"
some_func(1) # will display "tuple: (1,)"
some_func(1, 'A') # will display "tuple: (1, 'A')"
</syntaxhighlight>
This is useful to declare function that accept one or more required parameters and then an arbitrary number of parameters:
<syntaxhighlight lang='python'>
def some_func(required1, required2, *args):
  ...
</syntaxhighlight>
===Keyword Arguments===
Arguments can be specified by name, even in a different order from their definition in the function:
<syntaxhighlight lang='python'>
def menu(appetizer, entree, dessert):
    print(f"Appetizer: {appetizer}, entree: {entree}, dessert: {dessert}")
menu(dessert='cake', appetizer='salad', entree='chicken')
</syntaxhighlight>
Repeating a keyword argument with the same key is a syntax error.
Invoking a function with an improperly formed keyword argument (ex: <code>desert=</code>) is a syntax error.
Invoking the function with an inexistent keyword argument causes:
<font size=-1>
TypeError: my_function() got an unexpected keyword argument 'some_argument'
</font>
===Gather Keyword Arguments with **===
Two asterisks (**) indicate that a variable number of keyword arguments the function is invoked with are grouped together into a [[Python_Language_Dictionary#Overview|dictionary]] where the argument names are the keys and their values are the corresponding dictionary values. It is a common idiom in Python to call to call the dictionary parameter <code>kwargs</code>:
<syntaxhighlight lang='python'>
def some_func(**kwargs):
  print('keyword args:', kwargs)
some_func() # will display "keyword args: {}"
some_func(color='red') # will display "keyword args: {'color': 'red'}"
some_func(color='red', size='small') # keyword args: {'color': 'red', 'size': 'small'}
</syntaxhighlight>
This is useful to declare function that accept one or more required parameters and then an arbitrary number of positional parameters:
<syntaxhighlight lang='python'>
def some_func(required1, required2, **kwargs):
  ...
</syntaxhighlight>
If positional parameters are mixed with <code>[[#Gather_Positional_Arguments_with_.2A|*args]]</code> and <code>**kwargs</code>, they need to occur in that order.
==Pass by Value and Pass by Reference==
<font color=darkkhaki>
Are "primitives" passed by value? A copy is made?
How about more complex data structures?
https://www.geeksforgeeks.org/pass-by-reference-vs-value-in-python/
ints are immutable, so you'll have to build your own class with all the int's methods if you want a "mutable int"
</font>
Also see: {{Internal|Go_Functions#Pass-by-value_vs._pass-by-pointer_vs._pass-by-reference|Pass by Value vs. Pass by Reference vs. Pass by Pointer in Go}}
{{Internal|Go_Language#(No)_Reference_Variables|No Reference Variables in Go}}
==Dynamic Invocation==
Given access to the function or method instance, obtained via <code>[[Python_Introspection#getattr.28.29|getattr()]]</code> or <code>[[Python_Introspection#getmembers.28.29|inspect.getmembers()]]</code>, the function can be invoked by providing the parantheses-enclosing arguments to the function or method instance directly.
<font color=darkkhaki>
TODO: This is a dynamic function invocation:
<syntaxhighlight lang='py'>
TODO
</syntaxhighlight>
</font>
This is a static method invocation:


=Example=
<syntaxhighlight lang='py'>
<syntaxhighlight lang='py'>
def something(a, b):
class SomeClass:
  c = a + b
  @staticmethod
  return c
  def some_static_method(arg):
    ...
 
cls = ...
method = getattr(cls, 'some_static_method')
method('blue')
</syntaxhighlight>
==Applying a Function to an Iterable that Provides the Arguments==
<syntaxhighlight lang='py'>
def darken(color):
  return "dark " + color
 
colors = ['red', 'blue', 'green', 'yellow']
 
for c in map(darken, colors):
  print(c)
</syntaxhighlight>
Will print:
<font size=-2>
dark red
dark blue
dark green
dark yellow
</font>
 
=Passing Functions as Arguments=
<syntaxhighlight lang='python'>
def some_function():
    print('something')
 
def run_something(f):
    f()
 
run_something(some_function) # will display "something"
</syntaxhighlight>
 
This is very useful for mock testing.
 
Also see [[#Dynamic_Invocation|dynamic invocation]].
 
=Inner Functions=
A function can be defined within other function. Inner functions could be useful when performing some complex tax more than once within another function, to avoid loops and code duplication.
<syntaxhighlight lang='python'>
def outer(a, b):
    def inner(c, d):
        return c + d
    return inner(a, b)
 
print(outer(1, 2))
</syntaxhighlight>
=<span id='Closure'></span>Closures=
An inner function can act as a closure. A closure is a function that is dynamically generated by another function and can both change and remember the values of variables that were created outside itself.
<syntaxhighlight lang='python'>
def some_func(arg):
    # we define a closure, which is a function and remembers the argument of the enclosing function
    def closure():
        return f"we remember {arg}"
    return closure
 
a = some_func("A")  # we create a closure that remembers "A"
b = some_func("B")  # we create a closure that remembers "B"
 
print(a())  # Will display "we remember A"
print(b())  # Will display "we remember B"
</syntaxhighlight>
 
=<span id='Lambda'></span><span id='Lambdas'></span>Anonymous Functions (Lambdas)=
A lambda is an anonymous function expressed a a single statement. It can be used instead of a normal tiny function, by avoiding declaring the function and instead in-lining the behavior. Lambdas are also [[Python_Language_Functions#Callable|callable]].
<font size=-1>
<font color=darkgreen>lambda</font> <arg>: <one-line-statement>
</font>
Lambda takes one argument. <font color=darkkhaki>Does lambda always takes just one argument?</font> Everything between the colon and the terminating parentheses, or other separator in the invoking statement is the definition of the function.
 
The following example demonstrates the equivalency between a regular function and a lambda:
<syntaxhighlight lang='python'>
def regular_function(s):
    return s.capitalize()
 
def user_of_function(words, f):
    for w in words:
        print(f(w))
 
data = ['a', 'b', 'c']
user_of_function(data, regular_function)  # this statement uses a regular function
user_of_function(data, lambda w: w.capitalize())  # this statement uses an equivalent lambda, to the same effect
</syntaxhighlight>
 
A lambda may call a regular, multi-line function, previously defined. With the above example, the lambda calls the named function instead of implementing the behavior itself:
<syntaxhighlight lang='python'>
user_of_function(data, lambda w: regular_function(w))
</syntaxhighlight>
</syntaxhighlight>
Lambda functions are especially convenient in data analysis because there are many cases where data transformation functions will take functions as arguments. It is often less typing, and clearer, to pass a lambda function as opposed to writing a full-out function declaration or even assigning the lambda function to a local variable.
=Built-in Functions=
=Built-in Functions=
==<tt>type()</tt>==
These functions exist in the [[Python_Language#Built-in_Namespace|built-in namespace]]
<code>type()</code> returns the type of the argument.
 
==<tt>print()</tt>==
Built-in functions:
<code>print()</code>. If more comma-separated arguments are used, every comma adds a space.
* <span id='abs'></span><code>abs()</code>:
==<tt>input()</tt>==
* <span id='any'></span><code>any()</code>:
<code>input()</code> instructs Python to pause and read data from stdin. <code>input()</code> returns a string.
* <span id='all'></span><code>all()</code>:
* <span id='ascii'></span><code>ascii()</code>:
* <span id='bin'></span><code>bin()</code>:
* <span id='bool'></span><code>bool()</code>:
* <span id='bytearray'></span><code>bytearray()</code>:
* <span id='bytes'></span><code>bytes()</code>:
* <span id='callable'></span><code>callable()</code>:
* <span id='chr'></span><code>chr()</code>:
* <span id='compile'></span><code>compile()</code>:
* <span id='classmethod'></span><code>classmethod()</code>:
* <span id='complex'></span><code>complex()</code>:
* <span id='delattr'></span><code>delattr()</code>:
* <span id='dict'></span><code>dict()</code>:
* <span id='dir'></span><code>dir()</code>: [[Python_Language#Get_Names_in_a_Namespace_with_dir.28.29|Return the list of names in the specified namespace]]
* <span id='enumerate'></span><code>enumerate()</code>: [[Python_Language_List#Access_the_Index_and_the_Element_at_the_Same_Time_with_enumerate.28.29|Access the Index and the Element at the Same Time]]. <code>enumerate()</code> can be used with any collection.
* <span id='eval'></span><code>eval()</code>:
* <span id='exec'></span><code>exec()</code>:
* <span id='filter'></span><code>filter()</code>:
* <span id='format'></span><code>format()</code>:
* <span id='frozenset'></span><code>[[Python_Language_Set#frozenset.28.29|frozenset()]]</code>
* <span id='getattr'></span><code>[[Python_Introspection#getattr.28.29|getattr()]]</code>
* <span id='globals'></span><code>[[Python_Language#Global_Symbol_Table_and_globals.28.29|globals()]]</code>
* <span id='hasattr'></span><code>[[Python_Introspection#hasattr.28.29|hasattr()]]</code>
* <span id='help'></span><span id='help.28.29'></span><code>help()</code>: Display a function's [[#Docstring|docstring]].
* <span id='hash'></span>[[Eq_()_and_hash_()_in_Python#hash_.28.29|<code>hash()</code>]]
* <span id='input'></span><span id='input.28.29'></span><code>input()</code>: Instructs Python to pause and read data from stdin. <code>input()</code> returns a string.
<syntaxhighlight lang='python'>
<syntaxhighlight lang='python'>
s = input('this is the prompt')
s = input('this is the prompt')
print(s)
print(s)
</syntaxhighlight>
* <span id='id'></span><span id='id.28.29'></span>[[Python_Language_OOP#Identity_of_an_Object_Instance|<code>id()</code>]]
* <span id='isinstance'></span><code>[[Python Language OOP#isinstance|isinstance()]]</code>
* <span id='issubclass'></span><code>[[Python_Language_OOP#Checking_whether_a_Class_is_Subclass_of_Another_Class|issubclass()]]</code>
* <span id='iter'></span><code>iter()</code>: Creates an iterator instance, see [[Python_Iterators#Overview|Python Iterators]].
* <span id='list'></span><code>[[Python_Language_List#Create_a_List_or_Convert_other_Data_Type_to_a_List_with_list()|list()]]</code>: Converts a sequence to a list. See [[Python_Language_String#Convert_a_String_to_List|Convert a String to List]].
* <span id='locals'></span><code>[[Python_Language#Local_Symbol_Table_and_locals.28.29|locals()]]</code>
* <span id='len'></span><span id='len.28.29'></span><code>len()</code>: See [[Python_Language_String#The_Length_of_a_String|the length of a string]]. <font color=darkkhaki>TO PROCESS [[PyOOP]] "The len() function"</font>
* <span id='map'></span><code>map()</code>: [[#Applying_a_Function_to_an_Iterable_that_Provides_the_Arguments|Applying a Function to an Iterable that Provides the Arguments]]
* <span id='next'></span><code>next()</code>: Returns the next item in an iterator, see [[Python_Iterators#Overview|Python Iterators]].
* <span id='memoryview'></span><code>memoryview()</code>:
* <span id='object'></span><code>object()</code>:
* <span id='oct'></span><code>oct()</code>:
* <span id='open'></span><code>open()</code>: See [[File_Operations_in_Python#The_open.28.29_Built-in|File Operations in Python &#124; The <tt>open()</tt> Built-in]].
* <span id='ord'></span><code>ord()</code>:
* <span id='pow'></span><code>pow()</code>:
* <span id='print'></span><code>print()</code>: See [[Printing_to_stdout_in_Python#Overview|Printing to <tt>stdout</tt> in Python]]
* <span id='property'></span><code>[[Python_Language_OOP_Attributes_and_Properties#The_property.28.29_Built-in_Function|property()]]</code>
* <span id='range'></span><code>[[Python_Language#Generate_Number_Sequences_with_range.28.29|range()]]</code>
* <span id='repr'></span><code>repr()</code>:
* <span id='reversed'></span><code>reversed()</code>:  [[Python_Language_List#Iterate_over_a_List_in_Reversed_Order|Iterate over a List in Reversed Order]]. <code>reversed()</code> is a [[Python_Generators#Overview|generator]], it does not create the reversed sequence until materialized.
* <span id='round'></span><code>round()</code>:
* <span id='set'></span><code>[[Python_Language_Set#Creation_and_Initialization|set()]]</code>: Creates a [[Python_Language_Set#Overview|set]].
* <span id='setattr'></span><code>setattr()</code>:
* <span id='slice'></span><code>slice()</code>:
* <span id='sorted'></span><code>[[Python Built-In Function sorted()|sorted()]]</code>
* <span id='sum'></span><code>sum()</code>:
* <span id='staticmethod'></span><code>staticmethod()</code>:
* <span id='tuple'></span><code>tuple()</code>:
* <span id='type'></span><span id='type.28.29'></span><code>type()</code>: Returns the type of the argument.
* <span id='vars'></span><code>vars()</code>:
* <span id='zip'></span><code>[[Python_zip()#Overview|zip()]]</code>: [[Python_Language_Dictionary#From_a_Key_Sequence_and_a_Value_Sequence_with_zip()|Create a map from a key sequence and a value sequence]]
* <span id='import'></span><code>[[Python_Language_Modularization#import.28.29|__import__()]]</code>
* <span id='super'></span><code>super()</code>:
==<span id='Built-in_Type_Conversion_Functions'></span>Built-in Type Conversion (Casting) Functions==
* <span id='str'></span><code>[[Python_Language_String#Convert_other_Data_Types_to_Strings_with_str()|str()]]</code>:
* <span id='int'></span><code>[[Python_Language#int()|int()]]</code>:
* <span id='float'></span><code>[[Python_Language#float()|float()]]</code>:
* <span id='bool'></span><code>[[Python_Language#bool()|bool()]]</code>:
==Built-in Math Functions==
* <span id='divmod'></span><span id='divmod.28.29'></span><code>divmod()</code>:
* <span id='max'></span><code>max()</code>:
* <span id='min'></span><code>min()</code>:
=Callable=
<font color=darkkhaki>TO PROCESS: https://medium.com/swlh/callables-in-python-how-to-make-custom-instance-objects-callable-too-516d6eaf0c8d</font>
=Call Stack=
==Frame==
The frame is represented internally as a <code>FrameInfo</code>, which has a code context array.
Also see: {{Internal|Python_Introspection#Introspecting_the_Calls_Stack|Python Introspection &#124; Introspecting the Calls Stack}}
=Organizatorium=
<font color=darkkhaki>
* Document this invocation mode:
<syntaxhighlight lang='py'>
options = {
    "api_key": "<YOUR_API_KEY>",
    "app_key": "<YOUR_APP_KEY>",
}
initialize(**options)
</syntaxhighlight>
</syntaxhighlight>


=Other Functions=
</font>
<code>max('...')</code>, [[Python_Language#Type_Conversions|type conversion functions float(), int(), str()]]

Latest revision as of 18:54, 17 May 2024

Internal

TODO

  • TO PROCESS PyOOP "Extending built-in functions" + "Case study"
  • TO PROCESS PyOOP "An alternative to method overloading" + "Default arguments" + "Variable argument list" + "Unpacking arguments"
  • TO PROCESS PyOOP "Functions are objects too"

Overview

A function is a named or unnamed piece of code, which takes any number and type of input parameters and returns any number and type of output results. When a function returns more than one return value, it does that by packaging all the return values in a tuple. The function is first defined, then invoked, or called. As pretty much everything else, functions are objects, and they are "first-class citizens", in that they can be assigned to variables, used as arguments, and return them from other functions.

def f():
  pass

print(type(f))

will display:

<class 'function'>

Each function define its own namespace: variables defined inside a function are distinct from the variables with the same name defined in the main program.

Check whether an Object Instance is a Function

import types

def f():
    pass

assert isinstance(f, types.FunctionType)

Another way:

def f():
    pass

assert callable(f)

Function Definition

A function is defined by the reserved word def, followed by the function name, parentheses enclosing input parameters and a colon:

def function_name([parameters]):
  <function body>
def function_name(par1, par2, par3):
  <function body>

Two blank lines are expected after a function definition.

def add(a, b):
  c = a + b
  return c


print(add(1, 2))

Function Name Rules

The function name rules for function names is the same as for variable names.

Function Parameters

A parameter is a variable name used in the function definition. Each parameter translates to a variable private to the function. The parameters are handles for arguments for a particular function invocation.

A function can have positional parameters and keyword parameters. All positional arguments must be specified when calling the function. Keyword parameters are optional and are most commonly used to specify default values or optional parameters.

When invoked, the keyword arguments must follow the positional arguments, if any. Keyword arguments can be specified in any order, it does not need to match the order in which the corresponding keyword parameters have been declared.

Parameters are optional, a function may have no parameters. However, once a parameter is individually specified in the function definition, and that parameter does not have a default parameter value, a corresponding positional argument must be provided when the function is invoked, otherwise the invocation fails with:

TypeError: my_function() missing 1 required positional argument: 'arg1'

The function can be invoked with a variable number of arguments if the positional arguments are gathered with * or the keyword arguments are gathered with ** in the function definition, or both.

Also see:

Variables, Parameters, Arguments

Default Parameter Values

Default values can be declared for function parameters. The declared default value is used if the caller does not provide the corresponding argument. Naturally, if an argument is provided, it is used instead of the default value.

def make_item(name, color='green', size=1):
    return f"made {color} {name} in size {size}"

assert make_item("hat", "red") == "made red hat in size 1"

⚠️Default argument values are initialized when the function is defined, not when it is invoked. The default argument values are part of the function state, which is initialized only once when it is defined. This behavior is exemplified in the following example:

def m(arg, result=[]):
    result.append(arg)
    print(result)

m(1) # will display [1]
m(2) # will display [1, 2], the state of the list is preserved between the invocation  and is not reset on invocation.

Function Parameter Type Hints

https://docs.python.org/3/library/typing.html#module-typing
https://typing.readthedocs.io/en/latest/

Since 3.5, Python has support for type hints, which can be applied to both function parameters and return values. The Python runtime does not enforce function and variable type annotations. However, they can be used by third party tools such as type checkers, IDEs, linters, etc. This is a function whose parameter and return has type hints.

def some_func(arg: str) -> str:
  return "hello " + arg

def some_other_func(x: str | int) -> None:
  ...

Function Parameter Type Hinds and Default Values

def some_func(par1: str = 'blue') -> str:
    return par1.upper()

assert some_func() == "BLUE"

Spaces around '=' are expected. While the form without spaces is syntactically valid, it will be singled out as a weak warning "PEP 8: E252 missing whitespace around parameter equals".

def some_func(par1: str='blue') -> str: # PEP 8: E252 missing whitespace around parameter equals
    ...

Function Body

The body needs to be indented. It includes statements, an optional docstring and an optional return statement.

Docstring

https://www.python.org/dev/peps/pep-0257/

A docstring is a string declared at the beginning of the function body that serves as function documentation. Upon declaration, the string value is assigned to the __doc__ internal variable within the function instance. The string can be a one-line or multi-line and declared between single-quotes, double-quotes or triple quotes. The recommended style is to use triple double-quoted strings as docstrings. TO PROCESS: https://www.python.org/dev/peps/pep-0257/

def m(color='green'):
    """
    m() builds a widget of the given color.
    It gets the color and reports what it is going to build.
    I'll do so by:
        1. Reporting the color
        2. Building the widget

    Keyword arguments:
    color              -- the color (default green)
    size               -- the size (default 1)
    
    :return: the widget
    """
    print(f"I will build a {color} widget")
def m(color='green'):
    """
    This is what the function does.
    :param color: this is the color
    :return: the widget
    """
    print(f"I will build a {color} widget")

The function's docstring can be printed with the help() function, by passing the function name as argument. The output will be formatted.

help(m)

To print the raw docstring:

print(m.__doc__)

Return Value

The return statement, which is introduced by the return reserved word, ends the function execution and sends back the output result of the function.

def ...
  ...
  return optional_return_value

The compiler will not prevent to declare other statements after the return statement, they be never executed, though. If the function body does not contain a return statement, it will return None.

Returning Multiple Values

A function actually returns just one object, a tuple, which is then being unpacked into the result variables.

def shades(color):
  return 'dark ' + color, 'light ' + color, 'transparent ' + color


a, b, c = shades('red')

assert 'dark red' == a
assert 'light red' == b
assert 'transparent red' == c

Return Value Type Hints

https://docs.python.org/3/library/typing.html#module-typing

Since 3.5, Python has support for type hints, which can be applied to both function parameters and return values:

def some_func(par1: str) -> str:
    return par1.upper()

def some_other_func(x: str) -> int | None:
   ...

For more details on type hints, see Function Parameter Type Hints above.

Style

Exactly 2 blank lines are expected after a function definition. Also see:

Python Style

Function Invocation

A function is invoked by prepending parantheses to the name of the function and specifying arguments between parentheses. With no parentheses, Python treats the function like any other object.

Function Arguments

When the function is invoked, we pass an argument for each parameter declared in the function definition. An argument is a value that is passed into the function as function's input. When the function is called with arguments, the values of those argument are copied to their corresponding parameters inside the function. The argument can be another variable, defined in the block that invokes the function. Arguments are passed in parentheses and they are separated by commas. If the function has no parameters, we pass no arguments, but the parentheses still need to be provided.

function_name(arguments)
function_name(arg1, arg2, arg3)

Python is unusually flexible in the manner it handles function arguments. The most common option is to specify the arguments positionally. Another option is to use keyword arguments. Positional and keyword argument can be mixed, but in this case the positional arguments need to come first.

Also see:

Variables, Parameters, Arguments

Positional Arguments

When passing the arguments positionally, they map in order on the declared positional parameters. Although very common, a downside of positional arguments is that you need to remember the meaning of each position.

def menu(appetizer, entree, dessert):
    print(f"Appetizer: {appetizer}, entree: {entree}, dessert: {dessert}")

menu('salad', 'chicken', 'cake')

Even if only positional parameters were declared in the function definition, the function can be invoked with keyword parameters in any order. For the above function declaration, the following invocation is valid:

menu(dessert='chocolate', entree='steak', appetizer='salad')

Gather Positional Arguments with *

An asterisk (*) preceding a function parameter declaration indicates that a variable number of positional arguments the function is invoked with are grouped together into a tuple of parameter values. It is a common idiom in Python to call to call the tuple parameter args:

def some_func(*args):
  print('tuple:', args)

some_func() # will display "tuple: ()"
some_func(1) # will display "tuple: (1,)"
some_func(1, 'A') # will display "tuple: (1, 'A')"

This is useful to declare function that accept one or more required parameters and then an arbitrary number of parameters:

def some_func(required1, required2, *args):
  ...

Keyword Arguments

Arguments can be specified by name, even in a different order from their definition in the function:

def menu(appetizer, entree, dessert):
    print(f"Appetizer: {appetizer}, entree: {entree}, dessert: {dessert}")

menu(dessert='cake', appetizer='salad', entree='chicken')

Repeating a keyword argument with the same key is a syntax error.

Invoking a function with an improperly formed keyword argument (ex: desert=) is a syntax error.

Invoking the function with an inexistent keyword argument causes:

TypeError: my_function() got an unexpected keyword argument 'some_argument'

Gather Keyword Arguments with **

Two asterisks (**) indicate that a variable number of keyword arguments the function is invoked with are grouped together into a dictionary where the argument names are the keys and their values are the corresponding dictionary values. It is a common idiom in Python to call to call the dictionary parameter kwargs:

def some_func(**kwargs):
  print('keyword args:', kwargs)

some_func() # will display "keyword args: {}"
some_func(color='red') # will display "keyword args: {'color': 'red'}"
some_func(color='red', size='small') # keyword args: {'color': 'red', 'size': 'small'}

This is useful to declare function that accept one or more required parameters and then an arbitrary number of positional parameters:

def some_func(required1, required2, **kwargs):
  ...

If positional parameters are mixed with *args and **kwargs, they need to occur in that order.

Pass by Value and Pass by Reference

Are "primitives" passed by value? A copy is made?

How about more complex data structures?

https://www.geeksforgeeks.org/pass-by-reference-vs-value-in-python/


ints are immutable, so you'll have to build your own class with all the int's methods if you want a "mutable int"

Also see:

Pass by Value vs. Pass by Reference vs. Pass by Pointer in Go
No Reference Variables in Go

Dynamic Invocation

Given access to the function or method instance, obtained via getattr() or inspect.getmembers(), the function can be invoked by providing the parantheses-enclosing arguments to the function or method instance directly.

TODO: This is a dynamic function invocation:

TODO

This is a static method invocation:

class SomeClass:
  @staticmethod
  def some_static_method(arg):
     ...

cls = ...
method = getattr(cls, 'some_static_method')
method('blue')

Applying a Function to an Iterable that Provides the Arguments

def darken(color):
  return "dark " + color

colors = ['red', 'blue', 'green', 'yellow']

for c in map(darken, colors):
  print(c)

Will print:

dark red
dark blue
dark green
dark yellow

Passing Functions as Arguments

def some_function():
    print('something')

def run_something(f):
    f()

run_something(some_function) # will display "something"

This is very useful for mock testing.

Also see dynamic invocation.

Inner Functions

A function can be defined within other function. Inner functions could be useful when performing some complex tax more than once within another function, to avoid loops and code duplication.

def outer(a, b):
    def inner(c, d):
        return c + d
    return inner(a, b)

print(outer(1, 2))

Closures

An inner function can act as a closure. A closure is a function that is dynamically generated by another function and can both change and remember the values of variables that were created outside itself.

def some_func(arg):
    # we define a closure, which is a function and remembers the argument of the enclosing function
    def closure():
        return f"we remember {arg}"
    return closure

a = some_func("A")  # we create a closure that remembers "A"
b = some_func("B")  # we create a closure that remembers "B"

print(a())  # Will display "we remember A"
print(b())  # Will display "we remember B"

Anonymous Functions (Lambdas)

A lambda is an anonymous function expressed a a single statement. It can be used instead of a normal tiny function, by avoiding declaring the function and instead in-lining the behavior. Lambdas are also callable.

lambda <arg>: <one-line-statement>

Lambda takes one argument. Does lambda always takes just one argument? Everything between the colon and the terminating parentheses, or other separator in the invoking statement is the definition of the function.

The following example demonstrates the equivalency between a regular function and a lambda:

def regular_function(s):
    return s.capitalize()

def user_of_function(words, f):
    for w in words:
        print(f(w))

data = ['a', 'b', 'c']
user_of_function(data, regular_function)  # this statement uses a regular function
user_of_function(data, lambda w: w.capitalize())  # this statement uses an equivalent lambda, to the same effect

A lambda may call a regular, multi-line function, previously defined. With the above example, the lambda calls the named function instead of implementing the behavior itself:

user_of_function(data, lambda w: regular_function(w))

Lambda functions are especially convenient in data analysis because there are many cases where data transformation functions will take functions as arguments. It is often less typing, and clearer, to pass a lambda function as opposed to writing a full-out function declaration or even assigning the lambda function to a local variable.

Built-in Functions

These functions exist in the built-in namespace

Built-in functions:

s = input('this is the prompt')
print(s)

Built-in Type Conversion (Casting) Functions

Built-in Math Functions

  • divmod():
  • max():
  • min():

Callable

TO PROCESS: https://medium.com/swlh/callables-in-python-how-to-make-custom-instance-objects-callable-too-516d6eaf0c8d

Call Stack

Frame

The frame is represented internally as a FrameInfo, which has a code context array.

Also see:

Python Introspection | Introspecting the Calls Stack

Organizatorium

  • Document this invocation mode:
options = {
    "api_key": "<YOUR_API_KEY>",
    "app_key": "<YOUR_APP_KEY>",
}
initialize(**options)