Python Forum
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Back again on f-strings
#1
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. Wall

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
Reply
#2
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.
Reply
#3
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
If you can't explain it to a six year old, you don't understand it yourself, Albert Einstein
How to Ask Questions The Smart Way: link and another link
Create MCV example
Debug small programs

Reply
#4
(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
Reply
#5
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
If you can't explain it to a six year old, you don't understand it yourself, Albert Einstein
How to Ask Questions The Smart Way: link and another link
Create MCV example
Debug small programs

Reply
#6
(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
Reply
#7
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]
If you can't explain it to a six year old, you don't understand it yourself, Albert Einstein
How to Ask Questions The Smart Way: link and another link
Create MCV example
Debug small programs

Reply
#8
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.
Reply
#9
(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
Reply
#10
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
If you can't explain it to a six year old, you don't understand it yourself, Albert Einstein
How to Ask Questions The Smart Way: link and another link
Create MCV example
Debug small programs

Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  Trying to understand strings and lists of strings Konstantin23 2 764 Aug-06-2023, 11:42 AM
Last Post: deanhystad
  i'm back to f-strings, again Skaperen 4 1,086 Sep-21-2022, 08:13 PM
Last Post: Gribouillis
  Splitting strings in list of strings jesse68 3 1,767 Mar-02-2022, 05:15 PM
Last Post: DeaD_EyE
  Finding multiple strings between the two same strings Slither 1 2,521 Jun-05-2019, 09:02 PM
Last Post: Yoriz
  lists, strings, and byte strings Skaperen 2 4,232 Mar-02-2018, 02:12 AM
Last Post: Skaperen

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020