简体   繁体   中英

Python - How to Concatenate Strings in a Successive Way?

could you tell me whether there is a more elegant solution to my code what I need?

My little program takes text input from up to seven entry fields (exact number can be specified by the user). The input strings are then concatenated with interspersed fixed text elements. A single output in the form of a string is generated.

The program works perfectly fine. Yet, my programming strikes me as very inelegant and non-pythonic. Could you help me to improve, to foster my learning?

Two points:

1) I like to sum up the string length of all seven text inputs. I plainly added them, which is not elegant. How could I do it a better way?

string_length = len(str(E1.get())) + len(str(E2.get())) + len(str(E3.get())) +   len(str(E4.get())) + len(str(E5.get())) + len(str(E6.get())) + len(str(E7.get()))

2) Depending on the number of entry field which can be specified by the user using a Tkinter Scale S , Entry fields( En ) are enabled or disabled for taking input.

The Strings str(En.get()) derived from these Entry field are then concatenated with the fixed string elements partM in between to give rise to the string m . (The whole string is flanked with partL and partR on each side also.) I have done this by query the variable of the scale S (number of entry fields) via the get() method. The program then decides how to proceed via if / elif?

The higher the scale is set the more text elements are added. Yet this is highly similar task which strikes me as there might be a more straight forward solution.

This creates long lines of code and is not very amenable for scaling up the program for taking more Entry fields. How could I do it better?

code excerpt:

if S.get() == 1:
    if string_length == 22*S.get():        
        m = partL + str(E1.get()) + partR
        do_something()
    else:
        warning()

elif S.get() == 2:
    if string_length == 22*S.get():            
        m = partL + str(E1.get()) + partM + str(E2.get()) + partR
        do_something()
    else:
        warning()

elif S.get() == 3:
    if string_length == 22*S.get():
        m = partL + str(E1.get()) + partM + str(E2.get()) + partM + str(E3.get()) + partR
        do_something()
    else:
        warning()

I am very new to Python. I am also new to programming. Yet I am proud I write a little code that works fine and does something useful for me and others. I welcome any suggestions.

If your fields are E1 through E7 and each has a get method this should give you the total length of them:

fields = [E1, E2, E3, E4, E5, E6, E7]
total_length = sum(len(str(f.get())) for f in fields)

If you just want the values collected, this suffices:

fields = [E1, E2, E3, E4, E5, E6, E7]
values = [f.get() for f in fields]

i'll try answer the second part of the question since @gddc already has a very good answer to the first part.

To avoid code repeat you should look at common sections among your existing code.

if string_length == 22*S.get():
    ...
else:
    warning()

appears in all three cases. Therefore you can extract it to be:

if string_length != 22*S.get():
    warning()
else:     
    if S.get() == 1:
        ...
    elif S.get() == 2:
        ...
    elif S.get() == 3:
        ...
    do_something()

if you foresee to support more cases, you can expand with a function map

functions = {1 : one,
                2 : two,
                3 : three
}

def one():
    m = partL + str(E1.get()) + partR

def two():
    m = partL + str(E1.get()) + partM + str(E2.get()) + partR

def three():
    m = partL + str(E1.get()) + partM + str(E2.get()) + partM + str(E3.get())

and you call with:

functions[S.get()]()

The if string_length == 22*S.get() check happens regardless of the value of S.get() , and if it fails, you always raise a warning. To simplify your code and reduce code duplication, you can move that check outside of the logic that switches based on the value of S.get() :

if string_length != 22*S.get():
    warning()
else:
    if S.get() == 1:
        ...
    elif S.get() == 2:
        ...
    ...

If you make a list of fields instead of using 7 numbered variables, it's easy to use slicing and indexing to select the fields you want in a scalable way without needing separate clauses for every number. Also, the join method of strings allows you to join together a number of strings with a delimiter you specify:

if string_length != 22*S.get():
    warning()
else:
    middle = partM.join(str(field.get()) for field in fields[:S.get()])
    m = partL + middle + partR
    do_something()

Let's break down partM.join(str(field.get()) for field in fields[:S.get()]) :

fields should be the list of fields you made instead of using 7 variables.

fields[:S.get()] is slicing notation. It makes a list of the first S.get() elements of fields . (If fields doesn't have enough elements, the slice returns a copy of the whole list.)

str(field.get()) for field in fields[:S.get()] is a generator expression. It creates an iterator that loops over fields[:S.get()] and calls the get() method of every field in the list. This results in an iterator over the strings from the first S.get() fields. (Generator expressions must always appear in parentheses. If a generator expression is the only argument to a function, the function call parentheses fill this role. Otherwise, you must stick parentheses around it.)

partM.join(str(field.get()) for field in fields[:S.get()]) takes all the strings from that iterator and sticks them together, separated by partM between each pair of strings.

Well a way to join strings in to use the .join(insert something here) method. This is basically the string equivalent of the function .append(Insert something here).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM