Mam funkcję, która przyjmuje argument, który może być pojedynczą lub podwójną pozycją:
def iterable(arg)
if #arg is an iterable:
print "yes"
else:
print "no"
po to aby:
>>> iterowalne (("f", "f")) tak >>> iterowalne (["f", "f"]) tak >>> iterowalne ("ff") Nie
Problem polega na tym, że ciąg jest technicznie iterowalny, więc nie mogę po prostu złapać ValueError podczas próby arg[1]
. Nie chcę używać isinstance (), ponieważ to nie jest dobra praktyka (a przynajmniej tak mi powiedziano).
isinstance
jest to sposób na zrobienie tego w językach dynamicznie typowanych. Rzecz nie do codziennego użytku, ale OK w uzasadnionych przypadkach.Odpowiedzi:
Użyj instancji (nie rozumiem, dlaczego jest to zła praktyka)
import types if not isinstance(arg, types.StringTypes):
Zwróć uwagę na użycie StringTypes. Zapewnia, że nie zapomnimy o jakimś niejasnym typie łańcucha.
Z drugiej strony działa to również w przypadku pochodnych klas ciągów.
class MyString(str): pass isinstance(MyString(" "), types.StringTypes) # true
Możesz również rzucić okiem na to poprzednie pytanie .
Twoje zdrowie.
Uwaga: zachowanie zmieniło się w Pythonie 3 jako
StringTypes
ibasestring
nie są już zdefiniowane. W zależności od potrzeb możesz je zastąpićisinstance
przezstr
lub podzbiorem(str, bytes, unicode)
np. Dla użytkowników Cythona. Jak wspomniał @Theron Luhn , możesz również użyćsix
.źródło
isinstance
może to być jedyny sposób.do isinstance(arg, str)
. W przypadku wersji kompatybilnej wstecz rozważ użycie pythonhosted.org/six/#six.string_typestypes.StringTypes
nie jest dostępny w Python3. Jaka jest wartość w Python2?Od 2017 roku jest to przenośne rozwiązanie, które działa ze wszystkimi wersjami Pythona:
#!/usr/bin/env python import collections import six def iterable(arg): return ( isinstance(arg, collections.Iterable) and not isinstance(arg, six.string_types) ) # non-string iterables assert iterable(("f", "f")) # tuple assert iterable(["f", "f"]) # list assert iterable(iter("ff")) # iterator assert iterable(range(44)) # generator assert iterable(b"ff") # bytes (Python 2 calls this a string) # strings or non-iterables assert not iterable(u"ff") # string assert not iterable(44) # integer assert not iterable(iterable) # function
źródło
Od czasu Pythona 2.6, wraz z wprowadzeniem abstrakcyjnych klas bazowych,
isinstance
(używane w ABC, a nie w konkretnych klasach) jest teraz uważane za całkowicie akceptowalne. Konkretnie:from abc import ABCMeta, abstractmethod class NonStringIterable: __metaclass__ = ABCMeta @abstractmethod def __iter__(self): while False: yield None @classmethod def __subclasshook__(cls, C): if cls is NonStringIterable: if any("__iter__" in B.__dict__ for B in C.__mro__): return True return NotImplemented
To jest dokładna kopia (zmieniająca tylko nazwę klasy)
Iterable
zgodnie z definicją w_abcoll.py
(szczegół implementacjicollections.py
) ... powodem, dla którego działa to zgodnie z życzeniem, acollections.Iterable
nie jest, jest to, że ta ostatnia idzie o krok dalej, aby zapewnić, że ciągi są uważane za iterowalne, wywołującIterable.register(str)
jawnie tuż po tejclass
instrukcji.Oczywiście łatwo jest to rozszerzyć
__subclasshook__
, powracającFalse
przedany
wywołaniem innych klas, które chcesz konkretnie wykluczyć ze swojej definicji.W każdym razie, po zaimportowaniu tego nowego modułu takim
myiter
, jakimisinstance('ciao', myiter.NonStringIterable)
będzie , będzieFalse
iisinstance([1,2,3], myiter.NonStringIterable)
będzieTrue
, tak jak sobie zażyczysz - aw Pythonie 2.6 i nowszych jest to uważane za właściwy sposób realizacji takich kontroli ... zdefiniuj abstrakcyjną klasę bazową i sprawdźisinstance
to.źródło
isinstance('spam', NonStringIterable)
zwracaTrue
.__iter__
jest teraz zaimplementowany w łańcuchach w Pythonie 3. Tak więc mój „łatwy do rozszerzenia” akapit ma zastosowanie i np.if issublass(cls, str): return False
Musi zostać dodany na początku__subclasshook__
(jak również wszelkie inne klasy, które definiują,__iter__
ale w twoim sposobu myślenia nie można akceptować jako „iterowalnych elementów niebędących ciągami”).if issublass(C, str): return False
należy go dodać?Zdaję sobie sprawę, że to stary post, ale pomyślałem, że warto dodać moje podejście do potomności Internetu. Poniższa funkcja wydaje się działać w większości przypadków zarówno z Pythonem 2, jak i 3:
def is_collection(obj): """ Returns true for any iterable which is not a string or byte sequence. """ try: if isinstance(obj, unicode): return False except NameError: pass if isinstance(obj, bytes): return False try: iter(obj) except TypeError: return False try: hasattr(None, obj) except TypeError: return True return False
Sprawdza, czy nie jest ciągiem iterowalnym przez (mis) przy użyciu funkcji wbudowanej,
hasattr
która zgłosi a,TypeError
gdy jego drugi argument nie jest łańcuchem ani łańcuchem Unicode.źródło
Łącząc poprzednie odpowiedzi, używam:
import types import collections #[...] if isinstance(var, types.StringTypes ) \ or not isinstance(var, collections.Iterable): #[Do stuff...]
Nie jest to w 100% dowód na głupców, ale jeśli obiekt nie jest iterowalny, nadal możesz pozwolić mu przejść i wrócić do pisania na klawiaturze.
Edycja: Python3
types.StringTypes == (str, unicode)
. Odpowiednik Phythona3 to:if isinstance(var, str ) \ or not isinstance(var, collections.Iterable):
źródło
2.x
Zasugerowałbym:
hasattr(x, '__iter__')
lub w związku z komentarzem Davida Charlesa, który poprawił to dla Python3, co z:
hasattr(x, '__iter__') and not isinstance(x, (str, bytes))
3.x
wbudowany
basestring
typ abstrakcyjny został usunięty . Użyjstr
zamiast tego. Testr
ibytes
typy nie mają tyle wspólnego, funkcjonalność, aby uzasadnić wspólną klasę bazową.źródło
__iter__
że w Pythonie 3 są łańcuchy?Jak słusznie zauważyłeś, pojedynczy ciąg to sekwencja znaków.
Więc to, co naprawdę chcesz zrobić, to dowiedzieć się, jaki rodzaj sekwencji
arg
jest przy użyciu isinstance lub type (a) == str.Jeśli chcesz zrealizować funkcję, która przyjmuje zmienną ilość parametrów, powinieneś to zrobić w następujący sposób:
def function(*args): # args is a tuple for arg in args: do_something(arg)
function („ff”) i function („ff”, „ff”) będą działać.
Nie widzę scenariusza, w którym potrzebna jest funkcja isiterable () taka jak Twoja. To nie isinstance () jest złym stylem, ale sytuacjami, w których musisz użyć isinstance ().
źródło
type(a) == str
Należy unikać używania . Jest to zła praktyka, ponieważ nie bierze pod uwagę podobnych typów ani typów pochodzących zstr
.type
nie wspina się po hierarchii typów, podczas gdyisinstance
tak, dlatego lepiej jest użyćisinstance
.Aby wyraźnie rozwinąć świetną sztuczkę Alexa Martelliego
collections.py
i odpowiedzieć na niektóre związane z nim pytania: Aktualnym działającym rozwiązaniem w Pythonie 3.6+ jestimport collections import _collections_abc as cabc import abc class NonStringIterable(metaclass=abc.ABCMeta): __slots__ = () @abc.abstractmethod def __iter__(self): while False: yield None @classmethod def __subclasshook__(cls, c): if cls is NonStringIterable: if issubclass(c, str): return False return cabc._check_methods(c, "__iter__") return NotImplemented
i zademonstrowane
>>> typs = ['string', iter(''), list(), dict(), tuple(), set()] >>> [isinstance(o, NonStringIterable) for o in typs] [False, True, True, True, True, True]
Jeśli chcesz
iter('')
, na przykład, dodać do wykluczeń, zmodyfikuj wierszif issubclass(c, str): return False
być
# `str_iterator` is just a shortcut for `type(iter(''))`* if issubclass(c, (str, cabc.str_iterator)): return False
dostać
[False, False, True, True, True, True]
źródło