Busca cada char de una cadena para cada char en otro -- python campo con strings campo con python-3.x camp codereview Relacionados El problema

Search every char from a string for every char in another


8
vote

problema

Español

Estoy agregando una característica a un programa que permitirá al usuario agregar nuevos diccionarios como un archivo a la carpeta de archivos que se utilizará como una nota que toma el repositorio. Acabo de terminar el código para crear el nombre del archivo de la entrada del usuario, sin embargo, se siente como si fuera una forma más inteligente de realizar la tarea de lo que he hecho.

Lo siguiente es lo que he encontrado con eso que funciona, pero no sé si debería estar haciendo esto en un método más corto, con una función incorporada o una línea más inteligente.

¿Puedes explicar cualquier forma de que creas que esto podría mejorarse? He buscado otras soluciones, pero no estaban bien explicadas lo suficiente como para que pudiera entenderlas muy claramente.

Editar: Añadido mis importaciones al código. NOTA: La declaración de impresión en la parte inferior fue solo para probar el resultado del código y verlo en la consola. Se cambiará para volver cuando termine el resto del programa.

  from tkinter import * import sys import time import tkinter.messagebox import tkinter.simpledialog import json  finalFilename ='' def new_lib_prompt(event=None):     finalFilename =''     name_of_new_library = tkinter.simpledialog.askstring('Create New Note Library', 'Alphanumeric lowercase and "_" only', initialvalue = "Name_Here")     from string import ascii_letters,digits     validFilenameChars = "-_.() %s%s" %(ascii_letters, digits)     stringofnewname = str(name_of_new_library)     stringofvalid = str(validFilenameChars)     for i in stringofnewname:         if i in stringofvalid:             if i == ' ':                 new_i = '_'                 finalFilename += ''.join(new_i)             else:                 lower_i = i.lower()                 finalFilename += ''.join(lower_i)         else:             pass     print (finalFilename)   

Editar: 04/21/2017 2:30 PM

Gracias por todos los excelentes retroalimentación todos y gracias a holroy por su respuesta muy útil y pensada. Si alguien está interesado aquí es la solución que estaba buscando después de tomar un consejo desde algunas respuestas.

  valid_filename = '' # will be using this in another function later def new_lib_prompt(event=None):     global valid_filename     a_name = tkinter.simpledialog.askstring('Create New Note Library',                                             'Alphanumeric lowercase and "_" only',                                             initialvalue = "Name_Here")     VALID_CHARS = '-_.() {}{}'.format(ascii_letters, digits)     valid_filename = (''.join(c for c in a_name if c in VALID_CHARS)).replace(' ', '_').lower()   
Original en ingles

I am adding a feature to a program that will let the user add new dictionaries as a file to the file folder to be used as a note taking repository. I just got the code finished for creating the file name from user input, however it feels as if there should be a smarter way to perform the task than what I have done.

The following is what I have come up with that does work but I don't know if I should be doing this in a shorter method with either a built in function or a smarter one-liner.

Can you explain any ways that you think this could be improved? I have searched for other solutions but they were not well explained enough that I could understand them very clearly.

EDIT: Added my imports to code. Note: the print statement at the bottom was just to test the result of the code and see it in console. It will be changed to return when I finish up the rest of the program.

from tkinter import * import sys import time import tkinter.messagebox import tkinter.simpledialog import json  finalFilename ='' def new_lib_prompt(event=None):     finalFilename =''     name_of_new_library = tkinter.simpledialog.askstring('Create New Note Library', 'Alphanumeric lowercase and "_" only', initialvalue = "Name_Here")     from string import ascii_letters,digits     validFilenameChars = "-_.() %s%s" %(ascii_letters, digits)     stringofnewname = str(name_of_new_library)     stringofvalid = str(validFilenameChars)     for i in stringofnewname:         if i in stringofvalid:             if i == ' ':                 new_i = '_'                 finalFilename += ''.join(new_i)             else:                 lower_i = i.lower()                 finalFilename += ''.join(lower_i)         else:             pass     print (finalFilename) 

EDIT: 04/21/2017 2:30 PM

Thanks for all the great feedback everyone and thanks to holroy for his very useful and thought out response. If anyone is interested here is the solution I was looking for after taking advice from a few answers.

valid_filename = '' # will be using this in another function later def new_lib_prompt(event=None):     global valid_filename     a_name = tkinter.simpledialog.askstring('Create New Note Library',                                             'Alphanumeric lowercase and "_" only',                                             initialvalue = "Name_Here")     VALID_CHARS = '-_.() {}{}'.format(ascii_letters, digits)     valid_filename = (''.join(c for c in a_name if c in VALID_CHARS)).replace(' ', '_').lower() 
        
         
         

Lista de respuestas

6
 
vote
vote
La mejor respuesta
 

En primer lugar, abordemos algunas de las cuestiones de estilo más prominentes de su código, antes de bucear en un enfoque alternativo para resolverlo.

  • Tener importaciones en la parte superior del código : al principio no vi la línea 99887766655443316 , ya que estaba oculto en el código, pero esta no es una buena práctica de codificación. Pon esto en la parte superior del archivo, por lo que es fácil de ver.
  • tiene más espacio vertical - Agregue una línea adicional aquí y allá, para aumentar la legibilidad en su código. Típicamente, agregaría espacios frente a for path, words in corpus.iteritems(): … 7 bucles o for path, words in corpus.iteritems(): … 8 bloque para acentuar estos bloques.
  • es consistente en la denominación variable - Las variables de nombramiento es difícil, pero elige un estilo para denominación variable, y no lo mezcles como for path, words in corpus.iteritems(): … 9 , 99887766555443320 y set_of_unique_words1 . Esto hace que el código sea mucho más difícil de leer, lo que, a su vez, hace que sea más difícil mantener a largo plazo.
  • Las funciones deben devolver, no imprimir su resultado - Para tener una impresión de funciones, su salida suele ser un olor a código, y sería mejor que devuelva el resultado. Si por algún razonamiento está obligado a imprimirlo, tal vez lo indique que en el nombre de la función.
  • usando el operador set_of_unique_words2 se despeja a favor de str.format - consulte Formatear ejemplos para el método más nuevo y mejor para agregar argumentos a las cadenas.
  • ¿Por qué el set_of_unique_words3 - No he usado set_of_unique_words4 , por lo que es posible que lo necesite por alguna razón, pero es una especie de se ve superfluo para mí, ya que no usa el set_of_unique_words5 en la función en absoluto.
  • ¿Por qué el set_of_unique_words6 ? - ¿Por qué haces esto? ¿Esto está relacionado con set_of_unique_words7 de alguna manera también? ¿Realmente necesita "rinificar" la devolución de set_of_unique_words8 ?
  • ¡Únete cuando sea necesario! - el Únase a El comando se usa principalmente para unirse a las listas, así que unirse a una lista de un personaje es un trabajo adicional y no es necesario. Para agregar un solo carácter que pueda hacer 99887776655443329 . Vea a continuación un mejor uso de print_bag_of_words()0 .

Comentarios del algoritmo

Cuando resume su código hace las siguientes cosas:

  • lee una sugerencia usando print_bag_of_words()1
  • construye una constante de print_bag_of_words()2
  • convierte cualquier espacio en los guiones bajos, y
  • Bige a todos los demás personajes

tal como se cubrió antes de la importación en la parte superior, por lo que también debería declararse constantes. Cuando comience a validar el nombre, divide la cadena en caracteres individuales y use un 99887766555544333333, algo extraño, para agregar el nuevo nombre de archivo.

Sin embargo,

existe funciones que realizan ambas operaciones requeridas. Consultela Str.Lower () y str.replace () . Sin embargo, tenga en cuenta que, estos últimos reemplazan las subcadenas. En su caso print_bag_of_words()4 funcionaría, pero si reemplaza múltiples caracteres, consulte ¿Cómo reemplazar varios caracteres en una cadena?

La única parte no cubierta entonces es la parte de los caracteres válidos, y esta es la parte ligeramente fea de la nueva alternativa. Mi solución propuesta es hacer:

  print_bag_of_words()5  

y esto posiblemente requiere una pequeña explicación:

  • print_bag_of_words()6 - Esto espera una lista de algo, que se unirá por el separador en la parte delantera, esa es la cadena vacía.
  • print_bag_of_words()7 - Si el 998877766554433388 loops es válido, luego devuelve el 99887776655443339
  • set0 - dividir el set1 en caracteres individuales
  • set22 - Solo haz la acción (que es devuelta set3 ) del bucle set5 La expresión es cierta. Y la expresión verifica que el carácter set6 está dentro de la cadena 99 8776655443347 .

En otras palabras, las declaraciones anteriores se unen a todos los caracteres de set8 usando el separador de cadena vacío, para aquellos caracteres que están presentes en set9 . Eso es bastante bocado, ¡pero es una expresión muy útil!

Una versión más larga para escribir lo mismo es algo así (con un montón de casos de borde eliminados):

  dict0  

También estoy acostumbrado a separar cómo obtienes cosas (también conocido como el cuadro de diálogo dict1

Código refactorado

Entonces, ¿cómo se vería el código si se aplica todo esto? Tal vez algo así (no probado, ya que no tengo Tkinter disponible):

  dict2  

Tenga en cuenta que uno podría unirse al 99887766655443353 en menos líneas, pero mantenerlo legible también es algo bueno. Para el código real, lo más probable es que tenga el 99887766655443354 en una línea, y luego haga un 99887766655443355 en lugar de las dos líneas siguientes.

Como nota final, consulte también Gire una cadena en un nombre de archivo válido < / a> que encontré al investigar esta respuesta. Utiliza algunas cosas alternativas de Unicode, y tiene algunas otras ideas relacionadas sobre cómo manejar la validación de los nombres de archivos.

 

First of all let's address some of the more prominent style issues of your code, before diving into an alternate approach to solving it.

  • Have imports at top of code xe2x80x93xc2xa0At first I didn't see the from string import ... line as it was hidden in the code, but this is not good coding practice. Put this at the top of the file, so it is easy to see.
  • Have more vertical space xe2x80x93xc2xa0Please add an extra line here and there, to increase readability in your code. Typically I would add spaces in front of for loops or if block to accentuate these blocks.
  • Be consistent in variable naming xe2x80x93xc2xa0Naming variables is hard but choose one style for variable naming, and don't mix it like name_of_new_library, validFilenameChars and stringofnewname. This make the code a whole lot harder to read, which in turns makes it harder to maintain in the long run.
  • Functions should return, not print their result xe2x80x93xc2xa0To have a function print its output is usually a code smell, and it would be better to have it return the result. If by some reasoning it's required to print it out, I would maybe indicate that in the function name.
  • Using the % operator is depreceated in favor of str.formatxc2xa0xe2x80x93xc2xa0See format examples for the newer and better method of adding arguments into strings.
  • Why the event=None xe2x80x93xc2xa0I haven't used tkInter a lot, so you might need it for some reason, but it kind of looks superfluous to me as you don't use the event in the function at all.
  • Why the str(some_variable)? xe2x80x93 Why do you do this? Is this related to tkInter somehow as well? Do you really need to "stringify" the return from tkInter?
  • Only join when needed! xe2x80x93xc2xa0The join command is mainly used to join lists, so joining a list of one character is extra work, and not needed. To append a single character you could do txt += char. See below for a better use of join.

Algorithm comments

When summarized your code does the following things:

  • Reads a suggestion using tkInter
  • Builds a constant of validFilenameChars
  • Converts any spaces into underscores, and
  • lowercases all other characters

As covered before the import should be done at top, and so should also declaration of constants. When you start validifying the name you split the string into single characters, and use a somewhat strange += ''.join(new_i) to add onto the new filename.

There does however exist functions which does both of your required operations. See str.lower() and str.replace(). Do however note that, the latter replace substrings. In your case str.replace(' ', '_') would work, but if replacing multiple chars see how to replace multiple characters in a string?

The only part not covered then is the valid characters part, and this is the slightly ugly part of the new alternative. My proposed solution is to do:

''.join(c for c in suggested_name if c in VALID_CHARS) 

And this possibly requires a little explanation:

  • ''.join(_some list_) xe2x80x93xc2xa0This expects a list of something, which it will join together by the separator in front, that is the empty string.
  • c ... xe2x80x93 If the for loops is valid, then return the c
  • for c in suggested_name ... xe2x80x93xc2xa0Split the suggested_name into single characters
  • if c in VALID_CHARS xe2x80x93xc2xa0Only do the action (that is return c) from the for loop if the if expression is true. And the expression verifies that the character c is within the string VALID_CHARS.

In other words, the previous statements joins all characters from suggested_filename using the empty string separator, for those characters who are present in VALID_CHARS. That is quite a mouthful, but it is a very useful expression!

A longer version to write the same is something like (with a lot of edge cases removed):

my_list = [] for c in suggested_name:   if c in VALID_CHARS:     my_list.append(c)  my_separator = '' my_txt = my_list[0] for c in my_list[1:]:   my_txt += my_separator   my_txt += c 

I'm also accustomed to separating how you get stuff (aka the tkInter dialog) from validation/transformation of the result, so I would make a function to handle this.

Code refactored

So what would the code look like if applying all of this? Maybe something like this (untested as I don't have tkinter available):

from string import ascii_letters, digits VALID_CHARS = '-_.() {}{}'.format(ascii_letters, digits)  def get_valid_library_name(suggestion):   valid_filename = ''.join(c for c in suggestion if c in VALID_CHARS)   valid_filename = valid_filename.replace(' ', '_').lower()   return valid_filename  a_name = tkinter.simpledialog.askstring(                  'Create New Note Library',                   'Alphanumeric lowercase and "_" only',                  initialvalue = "Name_Here")  print('{} into {}'.format(a_name, get_valid_library_name(a_name))  filename = get_valid_library_name("MY someWHAT ugly suggestion#$!&&") print(filename)    # Would output "my_somewhat_ugly_suggestion" 

Note that one could join the valid_filename transformation into fewer lines, but keeping it readable is also a good thing. For real code I would most likely have the valid_filename = ''.join(..) on one line, and then do a return valid_filename.replace().lower() instead of the two next lines.

As a final note, also see Turn a string into a valid filename which I found when researching for this answer. It uses some alternate stuff from unicode, and has some other related ideas on how to handle validation of filenames.

 
 
         
         
6
 
vote

Cuerdas de construcción de la manera ingenua (con dict6 ) es muy ineficiente , use dict7 en su lugar.


Si desea mirar las cosas de manera eficiente, entonces el objetivo de la búsqueda debe ser un 99887776555443358 (o un 998877766555443359 ), en lugar de un list0 < / Código> (en su caso A list1 ). La razón de esto es que list2 (y list3 ) tiene (en promedio) Tiempo de búsqueda constante, mientras que 99887766555443364 tiene lineal.

Sin embargo, dado que el conjunto de caracteres es realmente pequeño, esto no importará demasiado.


Aparte de estos, ignorando algunos problemas de formato, llamadas innecesarias a list5 , implicaciones extrañamente colocadas; Tu código se ve bien.


list6655443366 ya es lineal en la longitud de list7 , pero si necesita un método, que es aproximadamente 99887776655443368 veces más rápido que eso, Luego, puede usar lo siguiente, que utiliza string.translate .

  list9  
 

Building strings the naive way (with +=) is very inefficient, use join instead.


If you want to look things up efficiently, then the target of the lookup should be a set (or a dict), rather than a list(in your case a str). The reason for this is that sets (and dicts) have (on average) constant lookup time, while lists have linear.

However since the character-set is really small, this won't matter too much.


Other than these, ignoring a few formatting issues, unneeded calls to str, strangely placed imports; your code looks fine.


''.join(c for c in a_name if c in VALID_CHARS)).replace(' ', '_').lower() is already linear in the length of a_name, but if you need a method, which is approximately 3.81 times faster than that, then you can use the following, which uses string.translate.

from string import ascii_letters,digits from collections import defaultdict  charList = ''.join(["-_.() ",ascii_letters, digits]) charDict = dict(zip(map(ord, charList)                    ,map(lambda c: '_' if c == ' ' else c.lower()                        ,charList))) charDict = defaultdict(lambda: '', charDict)  def createValidFileName(fileName):     return fileName.translate(charDict)  def newLibPrompt(event=None):     libName = tkinter.simpledialog.askstring(                 'Create New Note Library',                 'Alphanumeric lowercase and "_" only',                 initialvalue = "Name_Here")     return createValidFileName(libName) 
 
 
 
 
4
 
vote

Hay un par de cosas que puede hacer para mejorar su código real:

  • import Los módulos que necesita en la parte superior de su programa
  • no arrojan cuerdas a las cuerdas ( stringofnewname = str(name_of_new_library) ); Aquí, name_of_new_library ya es una cadena
  • use nombres de variables más descriptivos
  • No tiene que generar todas las letras minúsculas y mayúsculas si solo necesita las más bajas. Use ascii_lowercase en su lugar.
  • Usted dice que solo permite las bajas bajas alfanuméricas y "_", pero en realidad, deja que el usuario inserte otros caracteres. Eso es ambiguo.

El código que pensé se vería así:

  import tkinter from tkinter import simpledialog from string import ascii_lowercase, digits   def process_user_input(event):     name_of_new_library = simpledialog.askstring('Create New Note Library', 'Alphanumeric lowercase and "_" only',                                                  initialvalue="Name_Here").lower().replace(' ', '_')      valid_filename_chars = "{}{}_".format(ascii_lowercase, digits)     final_name = ''     for character in name_of_new_library:         if character in valid_filename_chars:             final_name += character      return final_name if final_name else 'custom_name'   if __name__ == '__main__':     root = tkinter.Tk()     print(process_user_input(root))   

Otros cambios que he hecho:

  • directamente lower ed y replace D La entrada del usuario para que podamos deshacernos de algunas condiciones
  • modificado el valid_filename_chars para que coincida con la descripción en askstring
  • ascii_lowercase9 ed a stringofnewname = str(name_of_new_library)0 Si el usuario solo ingresa caracteres ilegales
 

There are a couple of things you can do to improve your actual code:

  • import the modules that you need at the top of your program
  • don't cast strings to strings (stringofnewname = str(name_of_new_library)); here, name_of_new_library is already a string
  • use more descriptive variable names
  • you don't have to generate all lowercase and uppercase letters if you only need the lower ones. Use ascii_lowercase instead.
  • you say you only allow alphanumeric lowercases and "_", but you actually let the user insert other characters. That's ambiguous.

The code that I thought of would look like this:

import tkinter from tkinter import simpledialog from string import ascii_lowercase, digits   def process_user_input(event):     name_of_new_library = simpledialog.askstring('Create New Note Library', 'Alphanumeric lowercase and "_" only',                                                  initialvalue="Name_Here").lower().replace(' ', '_')      valid_filename_chars = "{}{}_".format(ascii_lowercase, digits)     final_name = ''     for character in name_of_new_library:         if character in valid_filename_chars:             final_name += character      return final_name if final_name else 'custom_name'   if __name__ == '__main__':     root = tkinter.Tk()     print(process_user_input(root)) 

Other changes that I've done:

  • directly lowered and replaced the user input so that we can get rid of some conditions
  • modified the valid_filename_chars so that it match the description in askstring
  • returned a custom_name if the user inputs only illegal characters
 
 
 
 

Relacionados problema

0  Producto cartesiano de dos tuplas - Python  ( Cartesian product of two tuples python ) 
Estoy resolviendo el ejercicio 4 de Discusión 3 de CS 61A (2012) de Berkley (2012) (consulte la página 4): Rellene la definición de cartesian_product . ...

5  Acelera a OpenGL 2D en Python3  ( Accelerate opengl 2d on python3 ) 
Estoy usando OpenGL para dibujar alrededor de 20 círculos. Cada círculo tiene 2 líneas, ~ 10 segmentos, y todos ellos tienen diferentes colores y longitudes. ...

5  Suma de todos los dígitos en una cadena  ( Sum of all digits in a string ) 
Dada una cadena, devuelva la suma de todos los números en la cadena, 0 si no hay ninguno. A continuación es mi solución para el problema anterior. Siento ...

5  Orden de número más grande en cadena  ( Largest number order in string ) 
Dada una cadena, suponiendo que la cadena sea solo números, reorganice la cadena a la que sea el mayor número posible. a continuación es mi solución al pr...

2  Algoritmo de libros que se ocupa de sumas de dígitos cuadrados  ( Books algorithm dealing with square digit sums ) 
un pequeño contexto: libro tiene 411 páginas Lea un número aleatorio de páginas en el primer día que es desconocido Número de páginas para leer e...

4  Calculadora binaria de Python  ( Python binary calculator ) 
Mi tarea fue construir una calculadora en la adición binaria de soporte de Python, etc. Para comenzar Definir un par de excepciones personalizadas: paper...

5  Excel a JSON Parser con http descargar  ( Excel to json parser with http download ) 
He estado trabajando en un proyecto ( enlace ) a Descargue una hoja de cálculo de Ransomware y propiedades conocidas y conviértase en JSON para que pueda cons...

7  Juego de Blackjack hecho en Python 3  ( Blackjack game made in python 3 ) 
Este es un juego de blackjack simple, terminé de hacer con Python. Espero que te guste y estoy abierto a cualquier sugerencia o crítica que me darías. NSUS...

2  Implementación de árboles de búsqueda de ternarios en Python 3  ( Ternary search tree implementation in python 3 ) 
He implementado un árbol de búsqueda ternario. Funciona bien. Pero si crees que algo necesita mejorarse, dígalo. Este código fue probado en Python 3.7.4. c...

3  Juego de Hangman escrito en Python 3.5  ( Hangman game written in python 3 5 ) 
Esto recopila las conjeturas del usuario para la palabra. El usuario tiene 8 conjeturas, una conjetura correcta y una suposición repetida no afecta al conteo ...




© 2022 respuesta.top Reservados todos los derechos. Centro de preguntas y respuestas reservados todos los derechos