Creating a string by iterating thru list,, dict and constants - python

Beginner issue here. I am sure there is a more pythonic way of doing this. basically I need to create a string using the contents of a list and a dict, plus I need to insert constants. I am trying to produce a end product for a function call via Eval. My current code works but it smells of 1980s BASIC (which ages me). I've looked at .join, zip and itter package but with no luck.
The list (argumentlist) contains argument names (such as open, close, length) and the dict (self.indicator_paremeters) contains all potential argument names along with their default value. So for example within the dict there is a key 'length' and its default value. In addition I need to concatenate '+' and commas, to create the end string.
Here is code sample to date.
def loop_thru_arguement_list_to_create_end_string(self, argument, resultant_argument_string):
if resultant_argument_string == "":
resultant_argument_string = argument + ' = ' + str(self.indicator_paremeters.get(
argument))
else:
resultant_argument_string = resultant_argument_string + ", " + argument + ' = ' + str(
self.indicator_paremeters.get(
argument))
return resultant_argument_string
This function is called from the loop here (need to rename that function):
def extract_argument_list_from_function(self, fullarguments) -> str:
resultant_argument_string = ""
argumentlist = fullarguments[0]
for argument in argumentlist:
resultant_argument_string = self.loop_thru_arguement_list_to_create_end_string(argument,
resultant_argument_string)
return resultant_argument_string
fullarguments = is a dict from a inspect.getfullargspec call. I only want the args, hence the [0].
All methods above are in a wider class.
self.indicator_paremeters is the dict holding all potential arguments.
The code above works fine but just doesn't feel right. Its the IF statement in particular which doesn't feel pythonic.

In my opinion you just need this:
def extract_argument_list_from_function(self, fullarguments: List) -> str:
res = ''
for arg in fullarguments[0]:
param = self.indicator_paremeters.get(arg)
res = f'{res}, {arg} = {param}' if res else f'{arg} = {param}'
return res
you can delete the loop_thru_arguement_list_to_create_end_string method

Related

Python: Split String and convert to other type

I have a function which could get a String formatted like this:
"true"^^<http://www.w3.org/2001/XMLSchema#boolean>
"100"^^<http://www.w3.org/2001/XMLSchema#int>
Now i want to split the String on the ^^ Characters and convert the first part of the string based on the second part. I also want to remove the " first before converting.
This is my code which i use for this:
def getValue(tObject):
toReturn = tObject.split("^^")
if len(toReturn) == 2:
if toReturn[1] == "<http://www.w3.org/2001/XMLSchema#boolean>":
return bool(toReturn[0].replace('"', ""))
elif toReturn[1] == "<http://www.w3.org/2001/XMLSchema#int>":
return int(toReturn[0].replace('"', ""))
return None
But i'm not so happy with it. Is there maybe a more elegant (pythonic) way to archive this?
You can use a regex, to
check if the given value is valid
retrieve the value to cast, and the way to cast
PATTERN = re.compile(r'"(.*)"\^\^<http:.*#(\w+)>')
types = {"boolean": bool, "int": int}
def getValue(value):
m = PATTERN.fullmatch(value)
return types[m.group(2)](m.group(1)) if m else None
Instead of if len(...) you could just try to unpack the result and except a ValueError. Then you can use a dict for the types and str.strip instead of str.replace:
types = {'boolean': bool, 'int': int}
try:
value, type_hint = tObject.split('^^')
except ValueError:
return None
else:
return types[type_hint.rstrip('>').rsplit('#', 1)[1]](value.strip('"'))
Firstly, you could remove return None, since the function returns None by default.
Secondly, you could use toReturn[1].endswith("boolean>") to match the end of the string, instead of matching the whole string with toReturn[1] == "<http://www.w3.org/2001/XMLSchema#boolean>". Same with the int string as well.
Thirdly, you could store the return value in one variable before the if..elif, then you don't have to calculate it twice for each condition.
Code:
def getValue(tObject):
toReturn = tObject.split("^^")
if len(toReturn) == 2:
return_value = toReturn[0].replace('"', "")
if toReturn[1].endswith("boolean>"):
return bool(return_value)
elif toReturn[1].endswith("int>"):
return int(return_value)
This might not be much of a logic improvement, but the code does look less cluttered now. If you wan't more terse, "pythonic" ways of doing this problem, the other answers might be more suitable.

How can I tell my function to do something specific thing when it receives no parameters?

So, I have the following function which should resemble the already implemented " print " function in Python. ( I know it is silly to make a function that only uses a pre-defined function but I am practicing with different things ) The thing that I want to do with my function is: make it act the same as the pre-defined one. That is, when the print function is called with no parameters, I would like it to print an empty line just as " print() " does. How do i do that ?
def print_str( string, how_many_times, tail ):
print ( string * how_many_times, end = tail )
print doesn't take a single string, it takes 0 or most strings as argument. end has a reasonable default. You can imagine a (simplified) definition of
def print(*args, end='\n'):
...
You need to do the same for yours. A reasonable default for how_many_times would be 1. If you don't want print_str to take multiple arguments, "" would be a reasonable default for string.
def print_str(string="", how_many_times=1, tail='\n'):
print(string * how_many_times, end=tail)
You can do something like this:
def myfunc(a=None):
if a == None:
print()
else:
print("hi")
If no arguments are presented it prints nothing, but if an argument is given it prints 'hi'.

How to use lists in recursive functions Python?

I'm writing a function that recursively traverses the file system, and returns a list of all files with the .txt extension.
The pass_test_func parameter is just a function that can be run and checked (i.e. is the file greater than 100 bytes, etc) - The nothing function (set as its default), simply returns its argument.
My implementation:
def visit(dname, pass_test_func=nothing):
directory = os.listdir(dname)
byte_list = []
for file in directory:
file_dir = os.path.join(dname, file)
if os.path.isfile(file_dir) and file_dir.lower().endswith('.txt'):
size = os.path.getsize(file_dir)
if pass_test_func(size):
byte_list.append(str(size) + ' ' + file_dir)
elif os.path.isdir(file_dir):
visit(file_dir, pass_test_func)
return byte_list
My problem is that when I recursively call visit in the following lines
elif os.path.isdir(file_dir):
visit(file_dir, pass_test_func)
the byte_list is cleared to empty again. I understand why this is happening, but have no idea how I would fix it. The list has to be defined within the definition of visit, so whenever I use recursion it will always be reset no matter what right? Maybe some other data structure is better suited, like a tuple or dictionary?
Your function returns byte_list, so just append the returned value when you make your recursive call, instead of throwing it away as you currently do:
elif os.path.isdir(file_dir):
byte_list += visit(file_dir, pass_test_func)
Add an optional argument that can be used in the recursive case:
# Using * makes byte_list keyword-only, so it can't be passed by normal callers by accident
def visit(dname, pass_test_func=nothing, *, byte_list=None):
directory = os.listdir(dname)
# When not passed explicitly, initialize as empty list
if byte_list is None:
byte_list = []
for file in directory:
file_dir = os.path.join(dname, file)
if os.path.isfile(file_dir) and file_dir.lower().endswith('.txt'):
size = os.path.getsize(file_dir)
if pass_test_func(size):
byte_list.append(str(size) + ' ' + file_dir)
elif os.path.isdir(file_dir):
# Pass explicitly to recursive call
visit(file_dir, pass_test_func, byte_list=byte_list)
return byte_list
As an alternative, as suggested by Blorgbeard, since you return byte_list, use that for your visit calls, changing only a single line in your original code:
visit(file_dir, pass_test_func)
to:
byte_list += visit(file_dir, pass_test_func)
This creates additional temporary lists, but that's usually not a big deal.

string not returned from function, but list is returned

I use a function to aggregate strings;
this function performs this concatenation via 2 different methods:
1) via string concatenation : CUM_STR = CUM_STR + str(IDX)
2) via List append : CUM_LST.append(IDX)
When returning from this function,
method 1 always gives an empty string for CUM1_TXT
method 2 correcly aggregates strings in the CUM_LST List
Here is a sample of this case :
CUM1_TXT = ''
CUM1_LIST = []
def modif(IDX,CUM_STR,CUM_LST):
CUM_STR = CUM_STR + str(IDX)
CUM_LST.append(IDX)
print CUM_STR
print CUM_LST
return CUM_STR,CUM_LST
for INDEX in range(10):
modif(INDEX,CUM1_TXT,CUM1_LIST)
print CUM1_TXT,CUM1_LIST
By the way, the fact to code the CUM_STR and CUM_LST on the return statement does not change anything at the result
Any help appreciated
let's take your method:
def modif(IDX,CUM_STR,CUM_LST):
CUM_STR = CUM_STR + str(IDX)
CUM_LST.append(IDX)
print CUM_STR
print CUM_LST
return CUM_STR,CUM_LST
CUM_STR is a str object. It is immutable, so modif cannot change the caller value.
CUM_LST is a list object. It is mutable. Appending within the method affects the caller value (altough not very nice to modify parameters, caller may not expect it)
and to top it all, you're returning the values, but you don't assign them in the caller: return values are lost on function return. Performing a return on parameters does not change the parameters.
If you allow me, I would rewrite it like this:
def modif(IDX,CUM_STR,CUM_LST):
return CUM_STR + str(IDX),CUM_LST+[IDX]
and you'd have to call it like this:
CUM1_TXT,CUM1_LIST = modif(INDEX,CUM1_TXT,CUM1_LIST)
You are discarding the return value of modif function, replace the call to modif with this line:
CUM1_TXT, _ = modif(INDEX,CUM1_TXT,CUM1_LIST)
Note that "_" is Python common practice to call variable which will not be used
C

splitting up strings to x amount of characters

I am trying to make a decrypter that decrypts code from the encrypter I made. I am getting this type error when I run the code though
getcrypt = ''.join(map(Decrypt.get,split_up_into_sixteen_chars(x_str)))
TypeError: split_up_into_sixteen_cjars() takes 0 positional arguments but 1 was given
I'm fairly new to programming and not sure whats causing this.
heres my code
Decrypt = {'1s25FF5ML10IF7aC' : 'A', 1s2afF5ML10I7ac' : 'a'} #I obviously have more than this but I'm trying to make it as simplified as possible
def split_up_into_sixteen_chars():
while len(x_str)>0:
v = x_str[:16]
print(v)
x_str = (input())
getcrypt = ''.join(map(Decrypt.get,split_up_into_sixteen_chars(x_str)))
print(getcrypt)
You have defined a function that takes no parameters:
def split_up_into_sixteen_chars():
yet you are passing it one:
split_up_into_sixteen_chars(x_str)
You need to tell Python that the function takes one parameter here, and name it:
def split_up_into_sixteen_chars(x_str):
The name used does not have to match the name that you pass in for the function call, but it does have to match what you use inside the function. The following function would also work; all I did was rename the parameter:
def split_up_into_sixteen_chars(some_string):
while len(some_string) > 0:
v = some_string[:16]
print(v)
This works because the parameter some_string becomes a local name, local to the function. It only exists inside of the function, and is gone again once the function completes.
Note that your function creates an infinite loop; the length of some_string will either always be 0, or always be longer than 0. The length does not change in the body of the loop.
The following would work better:
def split_up_into_sixteen_chars(some_string):
while len(some_string) > 0:
v = some_string[:16]
print(v)
some_string = some_string[16:]
because then we replace some_string with a shorter version of itself each time.
Your next problem is that the function doesn't return anything; Python then takes a default return value of None. Printing is something else entirely, print() writes the data to your console or IDE, but the caller of the function does not get to read that information.
In this case, you really want a generator function, and use yield. Generator functions return information in chunks; you can ask a generator for the next chunk one by one, and that is exactly what map() would do. Change the function to:
def split_up_into_sixteen_chars(some_string):
while len(some_string) > 0:
v = some_string[:16]
yield v
some_string = some_string[16:]
or even:
def split_up_into_sixteen_chars(some_string):
while some_string:
yield some_string[:16]
some_string = some_string[16:]
because an empty string is 'false-y' when it comes to boolean tests as used by while and if.
As your map(Decrypt.get, ...) stands, if split_up_into_sixteen_chars() yields anything that is not present as a key in Dycrypt, a None is produced (the default value for dict.get() if the key is not there), and ''.join() won't like that. The latter method can only handle strings.
One option would be to return a string default instead:
''.join(map(lambda chunk: Decrypt.get(chunk, ''), split_up_into_sixteen_chars(x_str)))
Now '', the empty string, is returned for chunks that are not present in Decrypt. This makes the whole script work for whatever string input you have:
>>> x_str='Hello world!'
>>> ''.join(map(lambda chunk: Decrypt.get(chunk, ''), split_up_into_sixteen_chars(x_str)))
''
>>> x_str = '1s25FF5ML10IF7aC'
>>> ''.join(map(lambda chunk: Decrypt.get(chunk, ''), split_up_into_sixteen_chars(x_str)))
'A'

Categories

Resources