How do Python functions handle the types of parameters that you pass in?
Python is strongly typed because every object has a type, every object knows its type, it's impossible to accidentally or deliberately use an object of a type "as if" it was an object of a different type, and all elementary operations on the object are delegated to its type.
This has nothing to do with names. A name in Python doesn't "have a type": if and when a name's defined, the name refers to an object, and the object does have a type (but that doesn't in fact force a type on the name: a name is a name).
A name in Python can perfectly well refer to different objects at different times (as in most programming languages, though not all) -- and there is no constraint on the name such that, if it has once referred to an object of type X, it's then forevermore constrained to refer only to other objects of type X. Constraints on names are not part of the concept of "strong typing", though some enthusiasts of static typing (where names do get constrained, and in a static, AKA compile-time, fashion, too) do misuse the term this way.
Call Python function passed as parameter without knowing if it takes any parameters
The preferred method of handling this is probably along these lines:
def doCommand(function, *args, **kwargs):
# do a thing
function(*args, **kwargs)
# do a thing
*args
and **kwargs
allow arbitrary arguments to be passed along to a method. As the name implies, these allow you to call doCommand
with an arbitrary number of arguments and keyword arguments as well that get passed onto function
.
Python best practice: How to handle function parameters that can be 2 types?
Don't. Make __init__
expect a DataFrame
, and make the caller responsible (perhaps via a class method) for converting a string to a DataFrame
before DataPipeline.__init__
is called.
class DataPipeline:
def __init__(self, data: DataFrame):
self.df = df
@classmethod
def from_string(cls, data: str):
return cls(spark.read.table(data))
p1 = DataPipeline(some_data_frame)
p2 = DataPipeline.from_string("...") # DataPipline(spark.read.table("..."))
Ryan Singer (supposedly; for as often as it is quoted, I've yet to find the original source) once said
So much complexity in software comes from trying to make one thing do two things.
Here, you are trying to make one thing (DataPipeline.__init__
) to two things (initialize a DataPipeline
using a string, and initialize a DataPipeline
with a DataFrame
).
For the refactoring, I chose to make the simplest case (accepting a DataFrame
) for __init__
, with the more complex logic of first parsing a string into a DataFrame
moved into a separate class method that calls __init__
with an appropriate data frame.
(Whether you want to make any explicit runtime checks for type validation to fail early, or just assume that the caller will accept the consequences of failing to pass a value of the correct type, is up to you.)
How to deal with functions that require too many parameters?
You can use **kwargs
in order to specify that your function can accept an arbitrary number of arguments. Although this means that you will be able to call the function by providing less arguments than what the Product
requires - however any issue will be reported directly from the constructor.
def creat_product(self, **kwargs) -> Product:
return Product(**kwargs)
And you should be able to call it exactly as before:
self.create_product(a='a', b='b', ...)
Note that in this way, you would be able to call the function by providing a dictionary as an argument. For instance,
my_args = {'a': 'val1', 'b': 'val2', ...}
create_product(**my_args)
As a side note, I guess that create_product
is an alternative constructor and thus it makes more sense to change it into a class method:
@classmethod
def creat_product(cls, **kwargs):
return cls(**kwargs)
In Python, is it possible to restrict the type of a function parameter to two possible types?
The term you're looking for is a union type.
from typing import Union
def f(parameter: Union[int, list]):
...
Union
is not limited to two types. If you ever have a value which is one of several known types, but you can't necessarily know which one, you can use Union[...]
to encapsulate that information.
How to specify input function parameter type and return type when passing the function as an argument to another function?
You can use typing.Callable
:
from typing import Callable
def bar(b: str, func: Callable[[str], int])->int:
return func(b)
How do I pass a variable by reference?
Arguments are passed by assignment. The rationale behind this is twofold:
- the parameter passed in is actually a reference to an object (but the reference is passed by value)
- some data types are mutable, but others aren't
So:
If you pass a mutable object into a method, the method gets a reference to that same object and you can mutate it to your heart's delight, but if you rebind the reference in the method, the outer scope will know nothing about it, and after you're done, the outer reference will still point at the original object.
If you pass an immutable object to a method, you still can't rebind the outer reference, and you can't even mutate the object.
To make it even more clear, let's have some examples.
List - a mutable type
Let's try to modify the list that was passed to a method:
def try_to_change_list_contents(the_list):
print('got', the_list)
the_list.append('four')
print('changed to', the_list)
outer_list = ['one', 'two', 'three']
print('before, outer_list =', outer_list)
try_to_change_list_contents(outer_list)
print('after, outer_list =', outer_list)
Output:
before, outer_list = ['one', 'two', 'three']
got ['one', 'two', 'three']
changed to ['one', 'two', 'three', 'four']
after, outer_list = ['one', 'two', 'three', 'four']
Since the parameter passed in is a reference to outer_list
, not a copy of it, we can use the mutating list methods to change it and have the changes reflected in the outer scope.
Now let's see what happens when we try to change the reference that was passed in as a parameter:
def try_to_change_list_reference(the_list):
print('got', the_list)
the_list = ['and', 'we', 'can', 'not', 'lie']
print('set to', the_list)
outer_list = ['we', 'like', 'proper', 'English']
print('before, outer_list =', outer_list)
try_to_change_list_reference(outer_list)
print('after, outer_list =', outer_list)
Output:
before, outer_list = ['we', 'like', 'proper', 'English']
got ['we', 'like', 'proper', 'English']
set to ['and', 'we', 'can', 'not', 'lie']
after, outer_list = ['we', 'like', 'proper', 'English']
Since the the_list
parameter was passed by value, assigning a new list to it had no effect that the code outside the method could see. The the_list
was a copy of the outer_list
reference, and we had the_list
point to a new list, but there was no way to change where outer_list
pointed.
String - an immutable type
It's immutable, so there's nothing we can do to change the contents of the string
Now, let's try to change the reference
def try_to_change_string_reference(the_string):
print('got', the_string)
the_string = 'In a kingdom by the sea'
print('set to', the_string)
outer_string = 'It was many and many a year ago'
print('before, outer_string =', outer_string)
try_to_change_string_reference(outer_string)
print('after, outer_string =', outer_string)
Output:
before, outer_string = It was many and many a year ago
got It was many and many a year ago
set to In a kingdom by the sea
after, outer_string = It was many and many a year ago
Again, since the the_string
parameter was passed by value, assigning a new string to it had no effect that the code outside the method could see. The the_string
was a copy of the outer_string
reference, and we had the_string
point to a new string, but there was no way to change where outer_string
pointed.
I hope this clears things up a little.
EDIT: It's been noted that this doesn't answer the question that @David originally asked, "Is there something I can do to pass the variable by actual reference?". Let's work on that.
How do we get around this?
As @Andrea's answer shows, you could return the new value. This doesn't change the way things are passed in, but does let you get the information you want back out:
def return_a_whole_new_string(the_string):
new_string = something_to_do_with_the_old_string(the_string)
return new_string
# then you could call it like
my_string = return_a_whole_new_string(my_string)
If you really wanted to avoid using a return value, you could create a class to hold your value and pass it into the function or use an existing class, like a list:
def use_a_wrapper_to_simulate_pass_by_reference(stuff_to_change):
new_string = something_to_do_with_the_old_string(stuff_to_change[0])
stuff_to_change[0] = new_string
# then you could call it like
wrapper = [my_string]
use_a_wrapper_to_simulate_pass_by_reference(wrapper)
do_something_with(wrapper[0])
Although this seems a little cumbersome.
how to convert while loop into a function or procedure and pass in new parameters python
Frankly, it would be better to get some tutorial to learn basis like functions.
def my_function(price):
while price > 1000:
price = price * 0.9
print("£", price)
and don't forget to run it
my_function(Motorbike_price)
Related Topics
How to Multiply Each Element in a List by a Number
Calling Dot Products and Linear Algebra Operations in Cython
Get Last Result in Interactive Python Shell
How to Apply a Function on Every Row on a Dataframe
How to Stop Numpy from Multithreading
Receive and Send Emails in Python
How to Read File with Space Separated Values in Pandas
Differencebetween I = I + 1 and I += 1 in a 'For' Loop
How to Read Two Lines from a File at a Time Using Python
Overriding "+=" in Python? (_Iadd_() Method)
How to Rotate Xticklabels in Matplotlib So That the Spacing Between Each Xticklabel Is Equal
Sql-Like Window Functions in Pandas: Row Numbering in Python Pandas Dataframe
Failedpreconditionerror: Attempting to Use Uninitialized in Tensorflow
Defining "Boolness" of a Class in Python
How to Extract an Ip Address from an HTML String