Python function's parameter default value is imported from another module - python

Let's consider the following example:
import module
def function(param=module.value):
pass
Is it good practice to set the default value of a function argument to an identifier imported from another module? I know it works, but I am asking rather about clean code approach and possible problems. I can see here a potential vulnerability: the value of the variable imported from module can be mutable in next release. What do you think about that?

I suggest setting the default to None then programatically test for that in your code:
import module
def function(param = None):
if param is None:
param = module.value
It could be that a straight assignment (which just copies a reference) is not appropriate.
For example, you mention that the class of object could change from immutable to mutable between releases. If that occurs then you probably need to use copy.deepcopy() rather than an assignment.
However, not all classes have their own __deepcopy__(), and so copying objects can be problematic. It could be that a new object should be created using the values from the original. It is hard to say what should be used without knowing the class of the object and what you are going to do with param.

You can write wrapper for your purpose. If the library change or deprecate value, you can still get the same value.
Just write a simple wrapper moduleWrapper
def getValue():
try:
return module.value
except:
return None
Or you can set your default value that you want and then you can set this function as param.
import moduleWrapper
def function(param=moduleWrapper.getValue()):
pass
This way if module change, you can still get your code work.

Related

Why should I set a function list argument as empty or none instead of using a normal variable

I've been looking up the information about why people should not use empty list as a function argument and pass none type as an argument and came up with a few questions.
Here is my code:
def add_employee(emp, emp_list=None):
if emp_list is None:
emp_list = []
emp_list.append(emp)
print(emp_list)
And here is code without second argument's type specified:
def add_employee(emp, emp_list):
emp_list.append(emp)
return emp_list
When I do not define emp_list as an empty list or none I can not utilize function's deafualt argument behavior: I can't call it like add_employee('Mark'), I had to add second variable to pass. Why is it good to have that backup default behaviour? Why couldn't I just leave it as emp_list.
Here is a great explanation of why you should avoid using mutable arguments in your defaults. Or at least use them sparingly: link
The general gist of it that the list will be created for the first time (in a location in memory) when you define the function. So as python reads the code and interprets it, you will end up creating that list once on the first 'read' of the function.
What this means is that you are not creating a fresh list each time you call that function, only when you define it.
To illustrate I will use the example from the link I shared above:
def some_func(default_arg=[]):
default_arg.append("some_string")
return default_arg
>>> some_func()
['some_string']
>>> some_func()
['some_string', 'some_string']
>>> some_func([])
['some_string']
>>> some_func()
['some_string', 'some_string', 'some_string']
If I understood your question correctly, you are asking why you're better off defining the emp_list explicitly rather than having it outside the function. In my mind it boils down to encapsulation. You're essentially trying to make sure that your function doesn't change the behavior of anything outside of its scope so you're forced to pass it things directly and be explicit. In practice if you have a variable outside of the scope named emp_list, it is absolutely fine to just append to it as long as you understand what the expected behavior is.
If you pass a list in the first bit of code as the emp_list, then the variable emp_list will contain your list a. The if statement will check if list a is None and since that check fails, it will skip the next line of assigning it a fresh empty list.

Python 3.7 Variable Chained Method Calls

I've run into a similar problem as referenced here - Dynamic Method Call In Python 2.7 using strings of method names
Here's an example of what my working method call looks like, the method at the end is based on a given data type, in this case .string_value:
tag.fields[field["key"]].string_value = field["value"]
However I won't always be assigning just strings as there are methods for other data types. I attempted a solution similar to the one referenced in the linked thread:
typer = getattr(datacatalog_v1.types.TagField, f"{field['type']}_value")
tag.fields[field["key"]].typer = field["value"]
With typer being my new dynamic method call, but it's not working. I'm receiving this as an error - 'TagField' object has no attribute 'typer'.
Any suggestions?
This is quite interesting. I'm not sure what package/datatype ur working on, however it looks like you have 2 issues.
First, getattr returns a string, and you can't call a string, e.g. 'python'()
Second, if you remove the () after getattr(), typer will be a string data, and you cant use it like that. In
tag.fields[field["key"]].typer
typer must be a method/attribute of some sort rather than string. The best way is to build if statement or dict, combine different value of typer with different method/attribute calls.
type_methods = {'string_value': tag.fields[field["key"]].string_value,
'int_value': tag.fields[field["key"]].int_value,
'json_value': tag.fields[field["key"]].json_value}
typer = getattr(datacatalog_v1.types.TagField, f"{field['type']}_value")
type_method[type] = field["value"]
update:
There is a setattr(object, name, value) function
typer = getattr(datacatalog_v1.types.TagField, f"{field['type']}_value")
setattr(tag.fields[field['key']], typer, field['value'])

returning functions from a method in python

I have tried looking into the documentation and google search , but I am unable to find out the significance of the [clazz] at the end of method. Could someone help me understand the meaning of the [clazz] at the end of the method? Thanks.
def get_context_setter(context, clazz):
return {
int: context.setFieldToInt,
datetime: context.setFieldToDatetime
}[clazz]
setFieldToInt and setFieldToDatetime are methods inside context class.
This function returns one of two things. It returns either context.setFieldToInt or context.setFieldToDatetime. It does so by using a dictionary as what would be a switch statement in other programming languages.
It checks whether clazz is a reference to the class int or a reference to the class datetime, and then returns the appropriate method.
It's identical to this code:
def get_context_setter(context, clazz):
lookup_table = {int: context.setFieldToInt,
datetime: context.setFieldToDatetime
}
context_function = lookup_table[clazz] # figure out which to return
return context_function
Using a dict instead of a switch statement is pretty popular, see Replacements for switch statement in Python? .
More briefly.
The code presented is expecting the class of some object as a parameter poorly named as clazz.
It's then using that class as a dictionary key.
They're essentially trying to accept two different types and call a method on the object type.
class is a keyword in Python.
The author of the code you show chose to use a strange spelling instead of a longer snake_case parameter name like obj_class.
The parameters really should have been named obj, obj_class
Or
instance, instance_class
Even better, the class really need not be a separate parameter.

how to extract value of a variable defined in a function using inspect module in python

I am trying to access a particular variable that is defined in a function in python module. I was able to get to the variable name(using inspect.getmembers()) function, I was able to extract all variables that are defined in the function of interest in the (co_varnames) attribute. How do I get the value assigned to the variable ? Here is the sample code:-
File 1:- input.py
class foo(self):
def some_fun():
some_var='abc'
File 2:- sample.py
imported_module = importlib.import_module(input)
all_varnames = inspect.getmembers(imported_module)
-->> need value of all_varnames['some_var']
You don't in that context.
Local variables are only present during the execution of the function, that is between it has been called and returned. Your only possibility would be to do that in a function that the function calles (or if it's a generator during iteration).
Anyhow, if you do it in that way it will still be non-valid again after the function has returned.
Thanks for all the comments and suggestions. #Thomas Wagenaar.#skyking.
This is what I finally ended up doing that, I defined the variable like this inside the function:-
def testMethod(self):
self.myvar = {'a':1,'b':2}
Then using a inspect module and getattr(), I could instantiate the class, call this testMethod(which failed with exception), but got to a point where the variable was initialized and then just read the variable. I dont know if the solution is "correct" way of doing things in python, but this little hack worked for me.

How to create module-wide variables in Python? [duplicate]

This question already has answers here:
Using global variables in a function
(25 answers)
Closed 3 years ago.
The community reviewed whether to reopen this question 4 months ago and left it closed:
Original close reason(s) were not resolved
Is there a way to set up a global variable inside of a module? When I tried to do it the most obvious way as appears below, the Python interpreter said the variable __DBNAME__ did not exist.
...
__DBNAME__ = None
def initDB(name):
if not __DBNAME__:
__DBNAME__ = name
else:
raise RuntimeError("Database name has already been set.")
...
And after importing the module in a different file
...
import mymodule
mymodule.initDB('mydb.sqlite')
...
And the traceback was:
...
UnboundLocalError: local variable 'DBNAME' referenced before assignment
...
Any ideas? I'm trying to set up a singleton by using a module, as per this fellow's recommendation.
Here is what is going on.
First, the only global variables Python really has are module-scoped variables. You cannot make a variable that is truly global; all you can do is make a variable in a particular scope. (If you make a variable inside the Python interpreter, and then import other modules, your variable is in the outermost scope and thus global within your Python session.)
All you have to do to make a module-global variable is just assign to a name.
Imagine a file called foo.py, containing this single line:
X = 1
Now imagine you import it.
import foo
print(foo.X) # prints 1
However, let's suppose you want to use one of your module-scope variables as a global inside a function, as in your example. Python's default is to assume that function variables are local. You simply add a global declaration in your function, before you try to use the global.
def initDB(name):
global __DBNAME__ # add this line!
if __DBNAME__ is None: # see notes below; explicit test for None
__DBNAME__ = name
else:
raise RuntimeError("Database name has already been set.")
By the way, for this example, the simple if not __DBNAME__ test is adequate, because any string value other than an empty string will evaluate true, so any actual database name will evaluate true. But for variables that might contain a number value that might be 0, you can't just say if not variablename; in that case, you should explicitly test for None using the is operator. I modified the example to add an explicit None test. The explicit test for None is never wrong, so I default to using it.
Finally, as others have noted on this page, two leading underscores signals to Python that you want the variable to be "private" to the module. If you ever do an import * from mymodule, Python will not import names with two leading underscores into your name space. But if you just do a simple import mymodule and then say dir(mymodule) you will see the "private" variables in the list, and if you explicitly refer to mymodule.__DBNAME__ Python won't care, it will just let you refer to it. The double leading underscores are a major clue to users of your module that you don't want them rebinding that name to some value of their own.
It is considered best practice in Python not to do import *, but to minimize the coupling and maximize explicitness by either using mymodule.something or by explicitly doing an import like from mymodule import something.
EDIT: If, for some reason, you need to do something like this in a very old version of Python that doesn't have the global keyword, there is an easy workaround. Instead of setting a module global variable directly, use a mutable type at the module global level, and store your values inside it.
In your functions, the global variable name will be read-only; you won't be able to rebind the actual global variable name. (If you assign to that variable name inside your function it will only affect the local variable name inside the function.) But you can use that local variable name to access the actual global object, and store data inside it.
You can use a list but your code will be ugly:
__DBNAME__ = [None] # use length-1 list as a mutable
# later, in code:
if __DBNAME__[0] is None:
__DBNAME__[0] = name
A dict is better. But the most convenient is a class instance, and you can just use a trivial class:
class Box:
pass
__m = Box() # m will contain all module-level values
__m.dbname = None # database name global in module
# later, in code:
if __m.dbname is None:
__m.dbname = name
(You don't really need to capitalize the database name variable.)
I like the syntactic sugar of just using __m.dbname rather than __m["DBNAME"]; it seems the most convenient solution in my opinion. But the dict solution works fine also.
With a dict you can use any hashable value as a key, but when you are happy with names that are valid identifiers, you can use a trivial class like Box in the above.
Explicit access to module level variables by accessing them explicity on the module
In short: The technique described here is the same as in steveha's answer, except, that no artificial helper object is created to explicitly scope variables. Instead the module object itself is given a variable pointer, and therefore provides explicit scoping upon access from everywhere. (like assignments in local function scope).
Think of it like self for the current module instead of the current instance !
# db.py
import sys
# this is a pointer to the module object instance itself.
this = sys.modules[__name__]
# we can explicitly make assignments on it
this.db_name = None
def initialize_db(name):
if (this.db_name is None):
# also in local function scope. no scope specifier like global is needed
this.db_name = name
# also the name remains free for local use
db_name = "Locally scoped db_name variable. Doesn't do anything here."
else:
msg = "Database is already initialized to {0}."
raise RuntimeError(msg.format(this.db_name))
As modules are cached and therefore import only once, you can import db.py as often on as many clients as you want, manipulating the same, universal state:
# client_a.py
import db
db.initialize_db('mongo')
# client_b.py
import db
if (db.db_name == 'mongo'):
db.db_name = None # this is the preferred way of usage, as it updates the value for all clients, because they access the same reference from the same module object
# client_c.py
from db import db_name
# be careful when importing like this, as a new reference "db_name" will
# be created in the module namespace of client_c, which points to the value
# that "db.db_name" has at import time of "client_c".
if (db_name == 'mongo'): # checking is fine if "db.db_name" doesn't change
db_name = None # be careful, because this only assigns the reference client_c.db_name to a new value, but leaves db.db_name pointing to its current value.
As an additional bonus I find it quite pythonic overall as it nicely fits Pythons policy of Explicit is better than implicit.
Steveha's answer was helpful to me, but omits an important point (one that I think wisty was getting at). The global keyword is not necessary if you only access but do not assign the variable in the function.
If you assign the variable without the global keyword then Python creates a new local var -- the module variable's value will now be hidden inside the function. Use the global keyword to assign the module var inside a function.
Pylint 1.3.1 under Python 2.7 enforces NOT using global if you don't assign the var.
module_var = '/dev/hello'
def readonly_access():
connect(module_var)
def readwrite_access():
global module_var
module_var = '/dev/hello2'
connect(module_var)
For this, you need to declare the variable as global. However, a global variable is also accessible from outside the module by using module_name.var_name. Add this as the first line of your module:
global __DBNAME__
You are falling for a subtle quirk. You cannot re-assign module-level variables inside a python function. I think this is there to stop people re-assigning stuff inside a function by accident.
You can access the module namespace, you just shouldn't try to re-assign. If your function assigns something, it automatically becomes a function variable - and python won't look in the module namespace.
You can do:
__DB_NAME__ = None
def func():
if __DB_NAME__:
connect(__DB_NAME__)
else:
connect(Default_value)
but you cannot re-assign __DB_NAME__ inside a function.
One workaround:
__DB_NAME__ = [None]
def func():
if __DB_NAME__[0]:
connect(__DB_NAME__[0])
else:
__DB_NAME__[0] = Default_value
Note, I'm not re-assigning __DB_NAME__, I'm just modifying its contents.

Categories