Sprawdź, czy łańcuch kończy się jednym z łańcuchów z listy

220

Jaki jest pythoniczny sposób pisania następującego kodu?

extensions = ['.mp3','.avi']
file_name = 'test.mp3'

for extension in extensions:
    if file_name.endswith(extension):
        #do stuff

Mam niejasną pamięć, że formożna uniknąć jednoznacznej deklaracji pętli i zapisać ją pod ifwarunkiem. Czy to prawda?

TheMeaningfulEngineer
źródło
2
Chociaż odpowiedź na to pytanie jest dobra, być może autor początkowo o tym myślał if any((file_name.endswith(ext) for ext in extensions)).
sapht

Odpowiedzi:

450

Chociaż nie jest to powszechnie znane, str.endswith akceptuje również krotkę. Nie musisz zapętlać.

>>> 'test.mp3'.endswith(('.mp3', '.avi'))
True
falsetru
źródło
10
Czy wiesz, dlaczego nie akceptuje listy, ale krotka? po prostu ciekawy
ilyail3,
2
@falsetru Link w odpowiedzi nie odpowiada wprost na to pytanie. Wspomina tylko, że może akceptować krotki, ale nie wyjaśnia, dlaczego nie akceptuje list. Ponieważ są to obie sekwencje, jedyną różnicą, którą potencjalnie widzę, jest to, że listy są zmienne, a krotki są niezmienne. Mogę się mylić, ale nie widzę żadnego innego powodu, dla którego zostało to wyraźnie stwierdzone.
KymikoLoco
4
Jeśli chcesz sprawdzić, czy ciąg znaków kończy się na literę:import string; str.endswith(tuple(string.ascii_lowercase))
Alex Willison
3
tylko uwaga, endswithakceptuje krotkę tylko dla Pythona 2.5 i nowszych
Akash Singh
1
Nigdy tego nie wiedziałem! To idealne!
fool4jesus
46

Po prostu użyj:

if file_name.endswith(tuple(extensions)):
Jon Clements
źródło
Prosty i skuteczny!
Ced
6

Weź rozszerzenie z pliku i sprawdź, czy znajduje się w zestawie rozszerzeń:

>>> import os
>>> extensions = set(['.mp3','.avi'])
>>> file_name = 'test.mp3'
>>> extension = os.path.splitext(file_name)[1]
>>> extension in extensions
True

Używanie zestawu, ponieważ złożoność czasowa wyszukiwania w zestawach wynosi O (1) ( docs ).

alecxe
źródło
8
Wystarczy wspomnieć o wydajności, w przypadku dość krótkich krotek .endswith()z krotką internowaną będzie szybsza niż wyszukiwanie zestawu
Jon Clements
@JonClements Wydaje mi się, że potrzebujesz specjalnej plakietki ze złotym komentarzem SO, aby robić niesamowite notatki na temat odpowiedzi i pytań :)
alecxe
Nie - wybieram się po odznakę „Stalking alecxe”;)
Jon Clements
2
Zauważ też, że w wersji 2.7 i nowszej możesz nam użyć składni matematycznej dla zestawów, {'.mp3','.avi'}pozwala to uniknąć dodatkowej konwersji typów i może być bardziej czytelna w zależności od tła („Może to powodować zamieszanie w słownikach i nie może być używane do tworzenia pustych zestawy).
Perkins
@JonClements pewnego dnia stanę się tak mądry jak ty :)
alecxe
3

Istnieją dwa sposoby: wyrażenia regularne i metody string (str).

Metody łańcuchowe są zwykle szybsze (~ 2x).

import re, timeit
p = re.compile('.*(.mp3|.avi)$', re.IGNORECASE)
file_name = 'test.mp3'
print(bool(t.match(file_name))
%timeit bool(t.match(file_name)

792 ns ± 1,83 ns na pętlę (średnia ± odchylenie standardowe z 7 przebiegów, każdy 1000000 pętli)

file_name = 'test.mp3'
extensions = ('.mp3','.avi')
print(file_name.lower().endswith(extensions))
%timeit file_name.lower().endswith(extensions)

274 ns ± 4,22 ns na pętlę (średnia ± odchylenie standardowe z 7 przebiegów, każda 1000000 pętli)

Igor A.
źródło
1

Mam to:

def has_extension(filename, extension):

    ext = "." + extension
    if filename.endswith(ext):
        return True
    else:
        return False
Thomas Wouters
źródło
1
Masz na myśli return filename.endswith(ext)? : P
Mr_and_Mrs_D
1

Właśnie się z tym spotkałem, szukając czegoś innego.

Poleciłbym skorzystać z metod zawartych w ospakiecie. Dzieje się tak, ponieważ możesz uczynić to bardziej ogólnym, kompensując każdy dziwny przypadek.

Możesz zrobić coś takiego:

import os

the_file = 'aaaa/bbbb/ccc.ddd'

extensions_list = ['ddd', 'eee', 'fff']

if os.path.splitext(the_file)[-1] in extensions_list:
    # Do your thing.
Xxxo
źródło
0

Inną możliwością może być użycie instrukcji IN:

extensions = ['.mp3','.avi']
file_name  = 'test.mp3'
if "." in file_name and file_name[file_name.rindex("."):] in extensions:
    print(True)
NeverHopeless
źródło
@ Rainald62, indexpowinien być rindexw takim przypadku.
NeverHopeless,
0

innym sposobem na zwrócenie listy pasujących ciągów jest

sample = "alexis has the control"
matched_strings = filter(sample.endswith, ["trol", "ol", "troll"])
print matched_strings
['trol', 'ol']
Akash Singh
źródło