Posts: 212
Threads: 25
Joined: Aug 2019
Hi!
It seems that I cannot fully understand f-strings. I do exercises and little programs trying to update them by using f-strings, but now and then, I keep having some problems.
For instance, I wrote this little program:
# atbs_04_commaCode_01.py
#
# It works.
#
def commaCode(list1):
print("'", end='')
print(*list1[:-1], sep=', ', end=",")
print(' and', list1[-1], end=".'\n\n")
spam1 = ['apples', 'bananas', 'tofu', 'cats']
commaCode(spam1)
eggs1 = ['bird', 'dolphin', 'whale', 'parrot', 'gorilla', 'dog']
commaCode(eggs1)
numbers1 = [1, 2, 3, 4, 5]
commaCode(numbers1) that works and produces the following output:
Output: 'apples, bananas, tofu, and cats.'
'bird, dolphin, whale, parrot, gorilla, and dog.'
'1, 2, 3, 4, and 5.'
>>>
1) Is there a way to simplify the 3 lines of print with f-strings?
2) Is there a good tutorial FOR DUMMIES or FOR NEWBIES on all the details of f-strings?
I like Metulburr's basic tutorial:
https://python-forum.io/Thread-Basic-f-s...xpressions
I wish there would be a longer, deeper one, but easy enough to follow and understand by thickheads like myself.
Thanks and all the best,
newbieAuggie2019
"That's been one of my mantras - focus and simplicity. Simple can be harder than complex: You have to work hard to get your thinking clean to make it simple. But it's worth it in the end because once you get there, you can move mountains."
Steve Jobs
Posts: 1,950
Threads: 8
Joined: Jun 2018
Oct-15-2019, 09:50 AM
(This post was last modified: Oct-15-2019, 09:50 AM by perfringo.)
Something like this? EDIT: initially didn't notice 'and'
>>> spam1 = ['apples', 'bananas', 'tofu', 'cats']
>>> print(f"'{', '.join(spam1)}.'")
'apples, bananas, tofu, cats.' Version with 'and':
>>> print(f"'{', '.join(spam1[:-1])}, and {spam1[-1]}.'")
'apples, bananas, tofu, and cats.' Newlines can be added outside the curled brackets.
I'm not 'in'-sane. Indeed, I am so far 'out' of sane that you appear a tiny blip on the distant coast of sanity. Bucky Katt, Get Fuzzy
Da Bishop: There's a dead bishop on the landing. I don't know who keeps bringing them in here. ....but society is to blame.
Posts: 8,156
Threads: 160
Joined: Sep 2016
Oct-15-2019, 10:00 AM
(This post was last modified: Oct-15-2019, 10:00 AM by buran.)
def print_nicely(iterable):
iterable = list(map(str, iterable))
if len(iterable) > 1:
print(f"'{', '.join(iterable[:-1])} and {iterable[-1]}'")
elif len(iterable) == 1:
print(f"'{iterable[0]}'")
else:
raise ValueError('Expecting non-empty iterable')
print_nicely(['apples', 'bananas', 'tofu', 'cats']) # print list
print_nicely(['bird']) # print single element list
print_nicely(range(4)) # print range object
print_nicely([]) Output: 'apples, bananas, tofu and cats'
'bird'
'0, 1, 2 and 3'
Traceback (most recent call last):
File "***", line 13, in <module>
print_nicely([])
File "***", line 8, in print_nicely
raise ValueError('Expecting non-empty iterable')
ValueError: Expecting non-empty iterable
by the way i may have seen a package on PyPI that does it but cannot find it at the moment
Posts: 212
Threads: 25
Joined: Aug 2019
(Oct-15-2019, 09:50 AM)perfringo Wrote: Something like this? EDIT: initially didn't notice 'and'
>>> spam1 = ['apples', 'bananas', 'tofu', 'cats']
>>> print(f"'{', '.join(spam1)}.'")
'apples, bananas, tofu, cats.' Thanks a lot!
I saw your version, just before you added the version with 'and' and I modified my code accordingly, but then I realized it didn't work the same way. I thought it was I had mistyped something, but later I noticed that it was not the code, but the lists (in plural) that I had. The 3 lists are different. The first 2 ones are formed by string elements, while the 3rd one is formed by integers, so I could understand then the error message:
def commaCode(list1):
print(f"'{', '.join(list1[:-1])}, and {list1[-1]}.'")
spam1 = ['apples', 'bananas', 'tofu', 'cats']
commaCode(spam1)
eggs1 = ['bird', 'dolphin', 'whale', 'parrot', 'gorilla', 'dog']
commaCode(eggs1)
numbers1 = [1, 2, 3, 4, 5]
commaCode(numbers1) Output: 'apples, bananas, tofu, and cats.'
'bird, dolphin, whale, parrot, gorilla, and dog.'
Error: Traceback (most recent call last):
File "C:/Users/User1/AppData/Local/Programs/Python/Python37/atbs_04_commaCode_02.py", line 9, in <module>
commaCode(numbers1)
File "C:/Users/User1/AppData/Local/Programs/Python/Python37/atbs_04_commaCode_02.py", line 2, in commaCode
print(f"'{', '.join(list1[:-1])}, and {list1[-1]}.'")
TypeError: sequence item 0: expected str instance, int found
But in theory, my function should be able to work with any list value passed to it.
How should I approached this inconvenience? Should I leave it then with the 3 print lines? Is it OK to leave it with the 3 print lines?
Thanks and all the best,
newbieAuggie2019
"That's been one of my mantras - focus and simplicity. Simple can be harder than complex: You have to work hard to get your thinking clean to make it simple. But it's worth it in the end because once you get there, you can move mountains."
Steve Jobs
Posts: 8,156
Threads: 160
Joined: Sep 2016
Oct-15-2019, 10:37 AM
(This post was last modified: Oct-15-2019, 10:37 AM by buran.)
I found it
https://github.com/jazzband/inflect
import inflect
p = inflect.engine()
print(p.join(['apples', 'bananas', 'tofu', 'cats'])) # print list
print(p.join(['bird'])) # print single element list
print(p.join(list(map(str, range(4))))) # print range object
print(p.join([])) Output: apples, bananas, tofu, and cats
bird
0, 1, 2, and 3
Posts: 212
Threads: 25
Joined: Aug 2019
Oct-15-2019, 10:48 AM
(This post was last modified: Oct-15-2019, 10:51 AM by newbieAuggie2019.)
(Oct-15-2019, 10:00 AM)buran Wrote: def print_nicely(iterable):
iterable = list(map(str, iterable))
if len(iterable) > 1:
print(f"'{', '.join(iterable[:-1])} and {iterable[-1]}'")
elif len(iterable) == 1:
print(f"'{iterable[0]}'")
else:
raise ValueError('Expecting non-empty iterable')
print_nicely(['apples', 'bananas', 'tofu', 'cats']) # print list
print_nicely(['bird']) # print single element list
print_nicely(range(4)) # print range object
print_nicely([]) Output: 'apples, bananas, tofu and cats'
'bird'
'0, 1, 2 and 3'
Traceback (most recent call last):
File "***", line 13, in <module>
print_nicely([])
File "***", line 8, in print_nicely
raise ValueError('Expecting non-empty iterable')
ValueError: Expecting non-empty iterable
by the way i may have seen a package on PyPI that does it but cannot find it at the moment
Thanks a lot!
Although I like your creative way of thinking, in this case, you have transformed the 4 lines I wanted to simplify, using f-strings:
def commaCode(list1):
print("'", end='')
print(*list1[:-1], sep=', ', end=",")
print(' and', list1[-1], end=".'\n\n") into actually 8 lines of code (maybe 6 if we eliminate the last 2 lines):
def print_nicely(iterable):
iterable = list(map(str, iterable))
if len(iterable) > 1:
print(f"'{', '.join(iterable[:-1])} and {iterable[-1]}'")
elif len(iterable) == 1:
print(f"'{iterable[0]}'")
else:
raise ValueError('Expecting non-empty iterable') I think that as I don't know about ' map()', I prefer Perfringo's one liner in this case, but I have not sorted out the problem with the numerical list yet.
Thanks and all the best,
(Oct-15-2019, 10:37 AM)buran Wrote: I found it
https://github.com/jazzband/inflect
import inflect
p = inflect.engine()
print(p.join(['apples', 'bananas', 'tofu', 'cats'])) # print list
print(p.join(['bird'])) # print single element list
print(p.join(list(map(str, range(4))))) # print range object
print(p.join([])) Output: apples, bananas, tofu, and cats
bird
0, 1, 2, and 3
Thanks a lot!
That looks to me much clearer than the other one.
Thanks and all the best,
newbieAuggie2019
"That's been one of my mantras - focus and simplicity. Simple can be harder than complex: You have to work hard to get your thinking clean to make it simple. But it's worth it in the end because once you get there, you can move mountains."
Steve Jobs
Posts: 8,156
Threads: 160
Joined: Sep 2016
Oct-15-2019, 11:09 AM
(This post was last modified: Oct-15-2019, 11:55 AM by buran.)
Mine is longer because it handles more cases, e.g. it can handle list with numerical values, range objects, generators, single element containers, empty containers, etc. If I just replicate your code (and inherent problems) it will be a one liner.
def print_nicely(iterable):
print(f"'{', '.join(iterable[:-1])} and {iterable[-1]}'") or to fully replicate your code (which will handle also the range/list with numerical values):
def print_nicely(iterable):
print(f"'{', '.join(str(item) for item in iterable[:-1])} and {str(iterable[-1])}'") for example try with your code to print generator expression or empty list or single element list
for empty list will get IndexError, for generator expression - TypeError, single element list will look ugly ', and bird.'
if you are not familiar with map() you can use list comprehension instead (I've seen that Guido is in favour of comprehension compared to map)
iterable = [str(item) for item in iterable]
Posts: 1,950
Threads: 8
Joined: Jun 2018
I agree with buran that his solution is more life-like than oneliner and therefore longer.
One should be defensive and/or generalise (duck-typing) when writing functions. In this particular case - you can through any iterable to it and it behaves as expected. Little longer code now, lot of time saved on debugging in the future.
I'm not 'in'-sane. Indeed, I am so far 'out' of sane that you appear a tiny blip on the distant coast of sanity. Bucky Katt, Get Fuzzy
Da Bishop: There's a dead bishop on the landing. I don't know who keeps bringing them in here. ....but society is to blame.
Posts: 212
Threads: 25
Joined: Aug 2019
Oct-15-2019, 12:01 PM
(This post was last modified: Oct-15-2019, 12:18 PM by newbieAuggie2019.)
(Oct-15-2019, 10:37 AM)buran Wrote: I found it
https://github.com/jazzband/inflect
import inflect
p = inflect.engine()
print(p.join(['apples', 'bananas', 'tofu', 'cats'])) # print list
print(p.join(['bird'])) # print single element list
print(p.join(list(map(str, range(4))))) # print range object
print(p.join([])) Output: apples, bananas, tofu, and cats
bird
0, 1, 2, and 3
Thanks a lot for your time and explanations!
I modified my code according to yours (after installing 'inflect'):
import inflect
def commaCode(list1):
p = inflect.engine()
print(p.join(list1)) # print list
spam1 = ['apples', 'bananas', 'tofu', 'cats']
commaCode(spam1)
eggs1 = ['bird', 'dolphin', 'whale', 'parrot', 'gorilla', 'dog']
commaCode(eggs1)
numbers1 = [1, 2, 3, 4, 5]
commaCode(numbers1) and when I ran it:
Output: apples, bananas, tofu, and cats
bird, dolphin, whale, parrot, gorilla, and dog
Error: Traceback (most recent call last):
File "C:/Users/User1/AppData/Local/Programs/Python/Python37/atbs_04_commaCode_04.py", line 12, in <module>
commaCode(numbers1)
File "C:/Users/User1/AppData/Local/Programs/Python/Python37/atbs_04_commaCode_04.py", line 5, in commaCode
print(p.join(list1)) # print list
File "C:\Users\User1\AppData\Local\Programs\Python\Python37\lib\site-packages\inflect.py", line 3789, in join
if "," in "".join(words):
TypeError: sequence item 0: expected str instance, int found
(Oct-15-2019, 11:09 AM)buran Wrote: Mine is longer because it handles more cases, e.g. it can handle list with numerical values, range objects, generators, single element containers, empty containers, etc. If I just replicate your code (and inherent problems) it will be a one liner. The only case your code would handle is the range/list with numerical values
def print_nicely(iterable):
print(f"'{', '.join(iterable[:-1])} and {iterable[-1]}'") or if you want to handle also range/list with numerical values
def print_nicely(iterable):
print(f"'{', '.join(str(item) for item in iterable[:-1])} and {str(iterable[-1])}'")
I guess that's why I get that
Error: TypeError: sequence item 0: expected str instance, int found
(Oct-15-2019, 11:09 AM)buran Wrote: for example try with your code to print generator expression or empty list or single element list
for empty list will get IndexError, for generator expression - TypeError, single element list will look ugly ', and bird.' Not sure what's a generator expression.
(Oct-15-2019, 11:09 AM)buran Wrote: if you are not familiar with map() you can use list comprehension instead (I've seen that Guido is in favour of comprehension compared to map)
iterable = [str(item) for item in iterable] I'm really ashamed of myself for my still lack of knowledge. Not sure either of what's a list comprehension, but I think I can get it with your example.
(Oct-15-2019, 10:37 AM)buran Wrote: I found it
https://github.com/jazzband/inflect
Very interesting! Thanks also for that!!!
Quote:[ ... ] convert numbers to words
[ ... ] # ADD CORRECT "a" OR "an" FOR A GIVEN WORD:
print("Did you want ", p.a(thing), " or ", p.an(idea))
# CONVERT NUMERALS INTO ORDINALS (i.e. 1->1st, 2->2nd, 3->3rd, etc.)
print("It was", p.ordinal(position), " from the left\n")
# CONVERT NUMERALS TO WORDS (i.e. 1->"one", 101->"one hundred and one", etc.)
# RETURNS A SINGLE STRING...
words = p.number_to_words(1234) # "one thousand, two hundred and thirty-four"
words = p.number_to_words(p.ordinal(1234)) # "one thousand, two hundred and thirty-fourth"
[ ... ] # JOIN WORDS INTO A LIST:
mylist = join(("apple", "banana", "carrot"))
# "apple, banana, and carrot"
mylist = join(("apple", "banana"))
# "apple and banana"
mylist = join(("apple", "banana", "carrot"), final_sep="")
# "apple, banana and carrot"
https://pypi.org/project/inflect/
Thanks and all the best,
newbieAuggie2019
"That's been one of my mantras - focus and simplicity. Simple can be harder than complex: You have to work hard to get your thinking clean to make it simple. But it's worth it in the end because once you get there, you can move mountains."
Steve Jobs
Posts: 8,156
Threads: 160
Joined: Sep 2016
inflect will not handle list with numerical values, range object or generator expressions. you need to preprocess these and convert to list/tuple with str elements. That's why you get the first error
|