condition within return statement - python

I am trying to create a function which takes as an argument a string and returns the same string but alternating upper and lower cases letters within the string. I know a longer solution to this, but I came up with the solution below which however is not working, and I am wondering why this does not work.
def myfunc(*args):
return [b.lower() if a%2 else b.upper() for a,b in enumerate(args)]
The above piece of code gives me as output a full uppercase string, for example:
>>> myfunc('Hello')
['HELLO']

Okay, first of all you function is not taking a single string now, but it can take several variables (because of the * you put in front of args), hence when you pass it the argument "Hello", it actually gets a tuple object like ('Hello', ). So in your for loop you actually iterate over that tuple instead of iterating over the characters of the string.
If you want to get a string, just remove the *.
def myfunc(args):
return "".join([b.lower() if a%2 else b.upper() for a,b in enumerate(args)])
Note that you operation returns not a string but a list of characters. The "".join([]) will concatenate the characters inside of your list into a string, separated by "" (i.e nothing)

You need to unpack the args (*args), but then this will return a list, so you need to join the string again too.
return "".join([b.lower() if a%2 else b.upper() for a,b in enumerate(*args)])
As noted in the comments, this only works if there is only one argument, if there isn't you may want to join the strings together, one such way is using chain, and then pass this in to enumerate instead
from itertools import chain
if len(args) > 1:
single_arg = chain.from_iterable(args)
return "".join(b.lower() if a%2 else b.upper() for a,b in enumerate(chain.from_iterable(single_arg)))

Related

Why do sequence for loops don't seem to work well in Python?

I'm trying to convert every string in a list to it's lowercase format using this function:
def lower_list(strings):
for string in strings:
string = string.lower()
return strings
But this implementation is not working, however when using the range funtion and I iterate using an index:
def lower_list(strings):
for i in range(len(strings)):
strings[i] = strings[i].lower()
return strings
I do get every element on my list converted to lowercase:
> print(lower_list(mylist))
['oh brother where art thou', 'hello dolly', 'monsters inc', 'fargo']
But with the first implementation I get the original list with Uppercase values, am I missing something important in how the for loop works?
In the first case, all you are doing is storing the lowercase value in a variable, but the list is untouched.
In the second case, you are actually updating the value in the list at that index.
You can also use a lambda function here:
def lower_list(strings):
return list(map(lambda x: x.replace(x, x.lower()), strings))
List comprehension is the easiest and the best:
def lower_list(strings):
return [string.lower() for string in strings]
The reason the first one does not work is that it is not actually modifying the value inside of the list, rather it is just affecting a copy of the value in the list. When you use the index-based function, it modifies the list itself.
def lower_list(strings):
for string in strings:
index_of_string = strings.index(string)
string = string.lower()
strings[index_of_string] = string
return strings
If you want the first one to work, maybe you can try something like that, but thats a bad way of doing it, just showing it as an example so maybe you'll understand better. You need the index of that string so you can replace it in the list. In your first attempt, you do not replace anything in the list.

Generate wordlist with known characters

I'm looking to write a piece of code in Javascript or Python that generates a wordlist file out of a pre-defined combination of characters.
E.g.
input = abc
output =
ABC
abc
Abc
aBc
abC
AbC
ABc
aBC
I have very basic knowledge of either so all help is appreciated.
Thank you
I'll assume that you're able to import Python packages. Therefore, take a look at itertools.product:
This tool computes the cartesian product of input iterables.
For example, product(A, B) returns the same as ((x,y) for x in A for y in B).
It looks quite like what you're looking for, right? That's every possible combination from two different lists.
Since you're new to Python, I'll assume you don't know what a map is. Nothing too hard to understand:
Returns a list of the results after applying the given function to each item of a given iterable (list, tuple etc.)
That's easy! So the first parameter is the function you want to apply and the second one is your iterable.
The function I applied in the map is as follows:
''.join
This way you set '' as your separator (basically no separator at all) and put together every character with .join.
Why would you want to put together the characters? Well, you'll have a list (a lot of them in fact) and you want a string, so you better put those chars together in each list.
Now here comes the hard part, the iterable inside the map:
itertools.product(*((char.upper(), char.lower()) for char in string)
First of all notice that * is the so-called splat operator in this situation. It splits the sequence into separate arguments for the function call.
Now that you know that, let's dive into the code.
Your (A, B) for itertools.product(A, B) are now (char.upper(), char.lower()). That's both versions of char, upper and lowercase. And what's char? It's an auxiliar variable that will take the value of each and every character in the given string, one at a time.
Therefore for input 'abc' char will take values a, b and c while in the loop, but since you're asking for every possible combination of uppercase and lowercase char you'll get exactly what you asked for.
I hope I made everything clear enough. :)
Let me know if you need any further clarification in the comments. Here's a working function based on my previous explanation:
import itertools
def func():
string = input("Introduce some characters: ")
output = map(''.join, itertools.product(*((char.upper(), char.lower()) for char in string)))
print(list(output))
As an additional note, if you printed output you wouldn't get your desired output, you have to turn the map type into a list for it to be printable.
A simple approach using generators, and no library code. It returns a generator (iterator-like object), but can be converted to a list easily.
def lU(s):
if not s:
yield ''
else:
for sfx in lU(s[1:]):
yield s[0].upper() + sfx
yield s[0].lower() + sfx
print list(lU("abc"))
Note that all the sub-lists of suffixes are not fully expanded, but the number of generator objects (each a constant size) that get generated is proportional to the length of the string.

Starred expression in ternary operator python

I wrote a python program to print the ascii value of up to every 3 numbers in a string or "ERROR" if the length is not divisible by three. I was golf the code when I ran into a SyntaxError.
Code:
c=input()
p=len(c)
c=[int(c[i:i+3])for i in range(0,len(c),3)]
print("ERROR"if p%3else*map(chr,c),sep='')#SyntaxError here
But this works:
c=input()
p=len(c)
c=[int(c[i:i+3])for i in range(0,len(c),3)]
print(*map(chr,c),sep='')
Putting a space before the * or after the 3 doesn't work. I could just use ''.join but it's one character longer. My question is why can't I use a starred expression in a ternary operator?
Because the * has to apply to the whole expression that produces the set of arguments, not a part of it, and not conditionally. Internally, CPython uses different bytecodes for calling with unpacking vs. normal calls, so your conditional would require it to change the byte code to call print based on the arguments to print, essentially rewriting what you wrote into pseudocode like:
if p % 3:
call(print, "ERROR", sep='')
else:
call_varargs(print, *map(chr, c), sep='')
which is beyond the compiler's capability.
If you wanted to make this work, you could do the following:
print(*(("ERROR",) if p%3 else map(chr,c)), sep='')
which ensures the whole ternary evaluates to an unpackable sequence and unpacks whatever survives unconditionally, avoiding the confusion.
print(*(["ERROR"] if p%3 else map(chr,c)),sep="!")
keep it outside of the ternary
The * expander transforms a single enumerable variable into individual variables. E.g.
li = [1,2,3]
print(*li)
produces: 1 2 3 instead of [1, 2, 3].
One value vs. multiple values
It appears to remove the brackets and pass a single string to print, but this is only an appearance, it actually replaces the single list variable by 3 variables and is actually equivalent to:
print(li[0], li[1], li[2])
It works because print accepts a variable number of arguments, so in our case it can deal with the single list or with these three integers.
The conditional expression is a one-value operator
However in your code you use the star operator within a conditional expression:
c = '065066067068'
p = len(c)
c = [int(c[i:i+3]) for i in range(0, p, 3)]
print('ERROR' if p%3 else *map(chr, c), sep='!')
print would be able to accept both evaluations of the expression, a single string value ('ERROR') or multiple char values from map.
But the conditional expression prevents returning multiple values according to the condition (p%3). The expression output has to be a single value. So you have no other choice than to return the list from map as an enumerable, and un-nest it only outside of the ternary operator, e.g. in the print statement.
A string is an enumerable, not a scalar value
However this solution now introduces another problem: Un-nesting will also convert the constant string ERROR into single chars, as a string is considered by Python an enumerable of single chars (as you know since you use this property for your input string). When the condition is true, the output would be:
E!R!R!O!R
Therefore the string must be first converted to an enumerable of strings, e.g. a tuple
Final solution
if p%3: s = ('ERROR',)
else: s = map(chr, c)
print(*s, sep='!')
The outputs will be:
A!B!C!D
ERROR

Error:string index out of range, defining a function

I'm practicing coding on codingbat.com since I'm a complete beginner in python, and here is one of the exercises:
Given a string, return a new string made of every other char starting with the first, so "Hello" yields "Hlo".
Here is my attempt at defining the function string_bits(str):
def string_bits(str):
char = 0
first = str[char]
for char in range(len(str)):
char += 2
every_other = str[char]
return (first + every_other)
Running the code gives an error. What's wrong with my code?
A different approach, with an explanation:
If you need to handle a sentence, where spaces would be included, you can do this using slicing. On a string slicing works as:
[start_of_string:end_of_string:jump_this_many_char_in_string]
So, you want to jump only every second letter, so you do:
[::2]
The first two are empty, because you just want to step every second character.
So, you can do this in one line, like this:
>>> " ".join(i[::2] for i in "Hello World".split())
'Hlo Wrd'
What just happened above, is we take our string, use split to make it a list. The split by default will split on a space, so we will have:
["Hello", "World"]
Then, what we will do from there, is using a comprehension, iterate through each item of the list, which will give us a word at a time, and from there we will perform the desired string manipulation per i[::2].
The comprehension is: (documentation)
i[::2] for i in "Hello World".split()
Finally, we call "".join (doc), which will now change our list back to a string, to finally give us the output:
"Hlo Wrd"
Check out the slicing section from the docs: https://docs.python.org/3/tutorial/introduction.html
The problem is that the char += 2 returns a value greater than len(str) as len(str)-1 (the range) + 2 is longer than the string. You could do:
def string_bits(string):
if len(string) == 2:
return string[0]
result = ''
for char in range(0,len(string),2):#range created value sin increments of two
result += string[char]
return result
A more succinct method would be:
def string_bits(string):
return string[::2]
You should avoid using 'str' as a variable name as it is a reserved word by Python.
Ok, for me:
You should not use str as a variable name as it is a python built-in function (replace str by my_str for example)
For example, 'Hello' length is 5, so 0 <= index <= 4. Here you are trying to access index 3+2=5 (when char = 3) in your for loop.
You can achieve what you want with the following code:
def string_bits(my_str):
result = ""
for char in range(0, len(my_str), 2):
result += my_str[char]
return result
The error you are getting means that you are trying to get the nth letter of a string that has less than n characters.
As another suggestion, strings are Sequence-types in Python, which means they have a lot of built-in functionalities for doing exactly what you're trying to do here. See Built-in Types - Python for more information, but know that sequence types support slicing - that is, selection of elements from the sequence.
So, you could slice your string like this:
def string_bits(input_string):
return input_string[::2]
Meaning "take my input_string from the start (:) to the end (:) and select every second (2) element"

Display the number of lower case letters in a string

This is what I have so far:
count=0
mystring=input("enter")
for ch in mystring:
if mystring.lower():
count+=1
print(count)
I figured out how to make a program that displays the number of lower case letters in a string, but it requires that I list each letter individually: if ch=='a' or ch=='b' or ch=='c', etc. I am trying to figure out how to use a command to do so.
This sounds like homework! Anway, this is a fun way of doing it:
#the operator module contains functions that can be used like
#their operator counter parts. The eq function works like the
#'=' operator; it takes two arguments and test them for equality.
from operator import eq
#I want to give a warning about the input function. In python2
#the equivalent function is called raw_input. python2's input
#function is very different, and in this case would require you
#to add quotes around strings. I mention this in case you have
#been manually adding quotes if you are testing in both 2 and 3.
mystring = input('enter')
#So what this line below does is a little different in python 2 vs 3,
#but comes to the same result in each.
#First, map is a function that takes a function as its first argument,
#and applies that to each element of the rest of the arguments, which
#are all sequences. Since eq is a function of two arguments, you can
#use map to apply it to the corresponding elements in two sequences.
#in python2, map returns a list of the elements. In python3, map
#returns a map object, which uses a 'lazy' evaluation of the function
#you give on the sequence elements. This means that the function isn't
#actually used until each item of the result is needed. The 'sum' function
#takes a sequence of values and adds them up. The results of eq are all
#True or False, which are really just special names for 1 and 0 respectively.
#Adding them up is the same as adding up a sequence of 1s and 0s.
#so, map is using eq to check each element of two strings (i.e. each letter)
#for equality. mystring.lower() is a copy of mystring with all the letters
#lowercase. sum adds up all the Trues to get the answer you want.
sum(map(eq, mystring, mystring.lower()))
or the one-liner:
#What I am doing here is using a generator expression.
#I think reading it is the best way to understand what is happening.
#For every letter in the input string, check if it is lower, and pass
#that result to sum. sum sees this like any other sequence, but this sequence
#is also 'lazy,' each element is generated as you need it, and it isn't
#stored anywhere. The results are just given to sum.
sum(c.islower() for c in input('enter: '))
You have a typo in your code. Instead of:
if my.string.lower():
It should be:
if ch.islower():
If you have any questions ask below. Good luck!
I'm not sure if this will handle UTF or special characters very nicely but should work for at least ASCII in Python3, using the islower() function.
count=0
mystring=input("enter:")
for ch in mystring:
if ch.islower():
count+=1
print(count)
The correct version of your code would be:
count=0
mystring=input("enter")
for ch in mystring:
if ch.islower():
count += 1
print(count)
The method lower converts a string/char to lowercase. Here you want to know if it IS lowercase (you want a boolean), so you need islower.
Tip: With a bit of wizardry you can even write this:
mystring= input("enter")
count = sum(map(lambda x: x.islower(), mystring))
or
count = sum([x.islower() for x in mystring])
(True is automatically converted to 1 and False to 0)
:)
I think you can use following method:
mystring=input("enter:")
[char.lower() for char in mystring].count( True ) )

Categories