How to set argument of an object? - python

so I want to know if there is a solution to make an argument which is not organised when I create an object.
class Joueur:
nbrJoueur=0
def __init__(self,c_pseudo,c_pv=25,c_sttat=0):
self.pseudo=c_pseudo
self.pv=c_pv
self.sttat=c_sttat
Joueur.nbrJoueur+=1
j1=Joueur("adel",250,0)
j2=Joueur("salah",c_sttat=10)
So like in j2 I had to make c_sttat=10 to avoid that it's the the c_pv which take the value 10.
So I want to avoid that problem when I have a lot of arguments how could I do that?

I think this may be what you want:
def __init__(self,c_pseudo, *, c_pv=25,c_sttat=0):
When you put * in the parameter list, all the arguments after that position must be given with keywords. So if you try to write
j2 = Joueur("joseph", 10)
you'll get an error because you didn't name the second argument. This forces you to indicate whether it's c_pv or c_sttat

Related

Python ast - getting function parameters and processing them

I'm trying to use the ast module in Python to parse input code, but am struggling with a lot of the syntax of how to do so. For instance, I have the following code as a testing environment:
import ast
class NodeVisitor(ast.NodeVisitor):
def visit_Call(self, node):
for each in node.args:
print(ast.literal_eval(each))
self.generic_visit(node)
line = "circuit = QubitCircuit(3, True)"
tree = ast.parse(line)
print("VISITOR")
visitor = NodeVisitor()
visitor.visit(tree)
Output:
VISITOR
3
True
In this instance, and please correct me if I'm wrong, the visit_Call will be used if it's a function call? So I can get each argument, however there's no guarantee it will work like this as there are different arguments available to be provided. I understand that node.args is providing my arguments, but I'm not sure how to do things with them?
I guess what I'm asking is how do I check what the arguments are and do different things with them? I'd like to check, perhaps, that the first argument is an Int, and if so, run processInt(parameter) as an example.
The value each in your loop in the method will be assigned to the AST node for each of the arguments in each function call you visit. There are lots of different types of AST nodes, so by checking which kind you have, you may be able to learn things about the argument being passed in.
Note however that the AST is about syntax, not values. So if the function call was foo(bar), it's just going to tell you that the argument is a variable named bar, not what the value of that variable is (which it does not know). If the function call was foo(bar(baz)), it's going to show you that the argument is another function call. If you only need to handle calls with literals as their arguments, then you're probably going to be OK, you'll just look instances of AST.Num and similar.
If you want to check if the first argument is a number and process it if it is, you can do something like:
def visit_Call(self, node):
first_arg = node.args[0]
if isinstance(first_arg, ast.Num):
processInt(first_arg.n)
else:
pass # Do you want to do something on a bad argument? Raise an exception maybe?

Python Pass function parameter as list identifier

I've created a function which can take a parameter which defines another call to manipulate a list. For example if I call sliprotor(Rotorid1, 1) directly, then the Rotorid1 list is manipulated as I want. Function below:
def sliprotor(rotorid,offset_qty):
for movers in range(26,0,-1):
rotorid[movers-1+offset_qty]=rotorid[movers-1]
for movers_refill in range(offset_qty):
rotorid[movers_refill]=rotorid[movers_refill+26]
However, if I try to call this 'indirectly' by building the list name and then executing it, 'rotorid' is not translated to the value, as it is when called directly.
The way I am doing this is
def set_curr_rotor(XX):
rotorid = "Rotorid"+str(XX)
return rotorid
rid1 = input("First rotor slip : ")
if(rid1):
sliprotor(set_curr_rotor(rid1),1)
So the 'indirect' call doesn't pass the value created by the set_curr_rotor function into the sliprotor function. The direct call does use the passed in value.
If I look in debug, you can see that it is directly calling rotorid[] as the list, not Rotorid1 or other Rotoridx and hence I get an index error.
....
File "", line 3, in sliprotor
rotorid[movers-1+offset_qty]=rotorid[movers-1]
IndexError: string index out of range
I could restructure the way I have the code, but I would prefer not to. Is there some method / scope issue I am missing? Is this just an intrinsic attribute of Python? I'm very new to Python so I'm just doing an exercise to model an Enigma machine.
Any help appreciated.
Ed
I'll assume that you have defined your rotors already, something like this:
Rotorid1 = list('abcdefghijklmnopqrstuvwxyz')
Rotorid2 = list('abcdefghijklmnopqrstuvwxyz')
And now you're reluctant to change this, because ... reasons.
That's fine. But you're still wrong. What you need to do is to create a larger data structure. You can do it like this:
Rotors = [ Rotorid1, Rotorid2, ... ]
Now you have a list-of-lists. The Rotors variable now contains all the various Rotorid variables. (Well, it references them. But that'll do.)
Instead of passing in the variable name as a handle to the rotor, you can simply pass in an index number:
def set_rotor(id):
global Current_rotor
Current_rotor = id
def slip_rotor(amount):
global Current_rotor
global Rotors
rotor = Rotors[Current_rotor]
for movers in range(26,0,-1):
rotor[movers-1+offset_qty]=rotor[movers-1]
# etc...
Also, be sure an look up slicings in Python - you can do a lot by manipulating sublists and substrings using slices.

Dynamically update all instances of multiple input function

I'm creating a program with a class that has 3 input attributes. The program calls a function that creates many of these objects with their inputs being given based on some other criteria not important to this question.
As I further develop my program, I may want to add more and more attributes to the class. This means that I have to go and find all instances of the function I am using to create these objects, and change the input arguments.
For example, my program may have many of these:
create_character(blue, pizza, running)
where inputs correspond to character's favorite color, food, and activity. Later, I may want to add a fourth input, such as favorite movie, or possibly a fifth or sixth or ninety-ninth input.
Do professional programmers have any advice for structuring their code so that they don't have to go through and individually change each line that the create_character function is called so that it now has the new, correct number of inputs?
Find and replace seems fine, but this makes error possible, and also seems tedious. I'm anticipating calling this function at least 50 times.
I can think of a few options for how you could design your class to make easier to extend later new kinds of "favorite" things.
The first approach is to make most (or all) of the arguments optional. That is, you should specify a default value for each one (which might be None if there's not a real value that could apply as a default). This way, when you add an extra argument, the existing places that call the function without the new argument will still work, they'll just get the default value.
Another option would be to use a container (like a dictionary) to hold the values, rather than using a separate variable or argument for each one. For instance, in your example could represent the character's favorites using a dictionary like favorites = {'color': blue, 'food': pizza, 'activity': running} (assuming the those values are defined somewhere), and then you could pass the dictionary around instead of the separate items. If you use the get method of the dictionary, you can also make this type of design use default values (favorites.get('movie') will return None if you haven't updated the code that creates the dictionary to add a 'movie' key yet).
You can take advantage of argument/keyword argument unpacking to support dynamically-changing function parameters. And also factory function/classes that generate the function you need:
def create_character(required1, required2, *opt_args, **kwargs):
""" create_character must always be called with required1 and required2
but can receive *opt_args sequence that stores arbitrary number of
positional args. kwargs hold a dict of optional keyword args """
for i, pos_arg in enumerate(opt_args):
# pos_arg walks opt_args sequence
print "position: {}, value: {}".format(i+3, pos_arg)
for keyword, value in kwargs:
print "Keyword was: {}, Value was: {}".format(keyword, value)
pos_args = (1,2,3)
create_character('this is required','this is also required', *pos_args)
""" position: 3, value: 1
position: 4, value: 2
position: 5, value: 3 """
a_dict = {
'custom_arg1': 'custom_value1',
'custom_arg2': 'custom_value2',
'custom_arg3': 'custom_value3'
}
create_character('this is required','this is also required', **a_dict)
""" Keyword was: custom_arg2, value: custom_value2
Keyword was: custom_arg3, value: custom_value3
Keyword was: custom_arg1, value: custom_value1 """
I really like the list or dictionary input method, but it was still messy and allowed for the possibility of error. What I ended up doing was this:
I changed the class object to have no inputs. Favorites were first assigned with random, default, or unspecified options.
After the class object was created, I then edited the attributes of the object, as so:
self.favorite_movie = "unspecified"
self.favorite_activity = "unspecified"
new_character = (character())
new_character.favorite_movie = "Dr. Strangelove"
I think that the downside to this approach is that it should be slower than inputting the variables directly. The upside is that this is easy to change in the future. Perhaps when the program is finished, it will make more sense to then convert to #Blckknight 's method, and give the input as a list or dictionary.

How to pass a parameter as a default?

I want to use the default of a parameter, but include its name in the call. I thought that setting the parameter to None would do that, but it doesn't.
For example:
def a(num=3):
print(num)
a(num=None) #returns "None", not "3" like I want it to.
How can I use the default of a named parameter while including it in the call? (Is it even possible?)
Just to explain why I would want to do something like this (since maybe it's a problem in my overall coding style):
I often times have code like this
def add(num, numToAdd=1):
return num+numToAdd
def addTwice(num, numToAdd=None):
for i in range(2):
num=add(num, numToAdd=numToAdd)
return num
addTwice(3) #throws an error instead of returning 5
What I want is for addTwice's numToAdd to always use the default of add's numToAdd, no matter what it is.
The reason: maybe later in the code I realize that it's better to add 2 as the default when executing add than it is to add 1.
So I change it to
def add(num, numToAdd=2):
return num+numToAdd
But, this won't help anything unless I can always specify in addTwice to use the default if it receives a default.
So, that's the rationale.
In other words: I'm having a function (the first function) call another function (the second function), and if the first function has to use a default value, I want it to default to being the default value on the second function. That way, I only have to change the default value on the second function (not every single function that calls it as well) in order to change the default functionality.
There's no built-in support to explicitly use the default value of a parameter. If you don't want to leave the parameter out at the call site, you'd have to retrieve the default value very manually:
import inspect
a(num=inspect.signature(a).parameters['num'].default)
which wouldn't be worth it in most cases.
def a(num=None):
if num is None:
num = 3
print(num)
a(num=None) # prints 3
a() # prints 3
... I guess ... maybe
alternatively to explain default params
def a(num=3):
print(num)
a(num=None) # prints None
a() # prints 3
No, you can't: that's a contradiction in terms. The default value is used if and only if you do not supply a value in the arguments list. If you supply a value, then the default cannot be used within the routine.
There is a great answer on how to do this (if you decide that the default-getting functionality I asked for is really what you want). But, I just wanted to point out that in practice I believe what I was trying to achieve is normally done with global variables.
That is, the usual way to do what I wanted to do is:
DEFAULT_NUM_TO_ADD = 1
def add(num, numToAdd=DEFAULT_NUM_TO_ADD):
return num+numToAdd
def addTwice(num, numToAdd=DEFAULT_NUM_TO_ADD):
for i in range(2):
num=add(num, numToAdd=numToAdd)
return num
addTwice(3) # returns 5
This allows me to quickly change the default, and the same default is used for both functions. It's explicit and very clear; it's pythonic.

Default parameter in python with objects

I know I can define default parameters in python, but can I do so with objects?
For example, I'd like to work with a p.expect object:
def exitDevice(ip, m='', sendExit=True):
if sendExit:
m.send('exit')
print "left device", ip
Is that the correct way to handle an object passed in as a default argument in Python? If not, how does one do so? Or if this is correct is there a better way to do so?
It's a bit tricky, since the default value has to be defined at the time your code is parsed, but you can always do something like this:
def exitDevice(ip,m=None,sendExit=True):
if m is None: m = getDefaultValueForM()
if sendExit: m.send ( 'exit' )

Categories

Resources