Saya memiliki fungsi yang mengambil argumen yang bisa berupa item tunggal atau item ganda:
def iterable(arg)
if #arg is an iterable:
print "yes"
else:
print "no"
yang seperti itu:
>>> iterable (("f", "f")) Iya >>> iterable (["f", "f"]) Iya >>> iterable ("ff") tidak
Masalahnya adalah string itu secara teknis dapat diulang, jadi saya tidak bisa begitu saja menangkap ValueError saat mencoba arg[1]
. Saya tidak ingin menggunakan isinstance (), karena itu bukan praktik yang baik (atau begitulah yang saya katakan).
isinstance
merupakan cara untuk melakukannya dalam bahasa yang diketik secara dinamis. Sesuatu yang tidak untuk digunakan setiap hari, tapi OK dalam kasus yang dibenarkan.Jawaban:
Gunakan isinstance (Saya tidak mengerti mengapa itu praktik yang buruk)
import types if not isinstance(arg, types.StringTypes):
Perhatikan penggunaan StringTypes. Ini memastikan bahwa kita tidak melupakan beberapa jenis string yang tidak jelas.
Sisi baiknya, ini juga berfungsi untuk kelas string turunan.
class MyString(str): pass isinstance(MyString(" "), types.StringTypes) # true
Selain itu, Anda mungkin ingin melihat pertanyaan sebelumnya ini .
Bersulang.
NB: perilaku diubah dalam Python 3
StringTypes
danbasestring
tidak lagi ditentukan. Tergantung pada kebutuhan Anda, Anda dapat menggantinyaisinstance
denganstr
, atau subset tuple(str, bytes, unicode)
, misalnya untuk pengguna Cython. Seperti yang disebutkan @Theron Luhn , Anda juga dapat menggunakansix
.sumber
isinstance
mungkin satu-satunya cara.do isinstance(arg, str)
. Untuk versi yang kompatibel dengan mundur, pertimbangkan untuk menggunakan pythonhosted.org/six/#six.string_typestypes.StringTypes
tidak tersedia di Python3. Berapakah nilai di Python2?Pada 2017, berikut adalah solusi portabel yang bekerja dengan semua versi Python:
#!/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
sumber
Sejak Python 2.6, dengan pengenalan kelas dasar abstrak,
isinstance
(digunakan pada ABC, bukan kelas konkret) sekarang dianggap dapat diterima. Secara khusus: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
Ini adalah salinan persis (hanya mengubah nama kelas)
Iterable
seperti yang didefinisikan dalam_abcoll.py
(detail implementasicollections.py
) ... alasan ini berfungsi seperti yang Anda inginkan, sementaracollections.Iterable
tidak, adalah bahwa yang terakhir bekerja ekstra untuk memastikan string dianggap iterable, dengan memanggilIterable.register(str)
secara eksplisit tepat setelahclass
pernyataan ini .Tentu saja mudah untuk menambah
__subclasshook__
dengan kembaliFalse
sebelumany
panggilan untuk kelas lain yang ingin Anda kecualikan secara khusus dari definisi Anda.Bagaimanapun, setelah Anda mengimpor modul baru ini sebagai
myiter
,isinstance('ciao', myiter.NonStringIterable)
akanFalse
, danisinstance([1,2,3], myiter.NonStringIterable)
akanTrue
, seperti yang Anda minta - dan dalam Python 2.6 dan yang lebih baru ini dianggap cara yang tepat untuk mewujudkan pemeriksaan tersebut ... definisikan kelas dasar abstrak dan periksaisinstance
.sumber
isinstance('spam', NonStringIterable)
kembaliTrue
.__iter__
yang sekarang diterapkan di string di Python 3. Jadi saya "mudah untuk menambah" ayat menjadi berlaku dan misalnyaif issublass(cls, str): return False
perlu ditambahkan pada awal__subclasshook__
(serta setiap kelas lain yang menentukan__iter__
tetapi dalam Anda pola pikir tidak boleh diterima sebagai "non-string iterables").if issublass(C, str): return False
harus ditambahkan?Saya menyadari ini adalah posting lama tetapi saya pikir itu layak menambahkan pendekatan saya untuk keturunan Internet. Fungsi di bawah ini tampaknya berfungsi untuk saya dalam banyak situasi dengan Python 2 dan 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
Ini memeriksa non-string yang dapat diulang oleh (mis) menggunakan built-in
hasattr
yang akan memunculkanTypeError
ketika argumen keduanya bukan string atau string unicode.sumber
Dengan menggabungkan balasan sebelumnya, saya menggunakan:
import types import collections #[...] if isinstance(var, types.StringTypes ) \ or not isinstance(var, collections.Iterable): #[Do stuff...]
Bukan 100% bukti bodoh, tetapi jika sebuah objek bukan merupakan iterable Anda masih bisa membiarkannya lewat dan kembali mengetik.
Edit: Python3
types.StringTypes == (str, unicode)
. Setara Phython3 adalah:if isinstance(var, str ) \ or not isinstance(var, collections.Iterable):
sumber
2.x
Saya akan menyarankan:
hasattr(x, '__iter__')
atau mengingat komentar David Charles yang mengubah ini untuk Python3, bagaimana dengan:
hasattr(x, '__iter__') and not isinstance(x, (str, bytes))
3.x
basestring
tipe abstrak bawaan telah dihapus . Gunakanstr
sebagai gantinya. Jenisstr
danbytes
tidak memiliki fungsi yang cukup umum untuk menjamin kelas dasar bersama.sumber
__iter__
ada string di Python 3?Seperti yang Anda tunjukkan dengan benar, satu string adalah urutan karakter.
Jadi hal yang benar-benar ingin Anda lakukan adalah mencari tahu seperti apa urutan
arg
itu dengan menggunakan isinstance atau type (a) == str.Jika Anda ingin mewujudkan fungsi yang mengambil sejumlah parameter variabel, Anda harus melakukannya seperti ini:
def function(*args): # args is a tuple for arg in args: do_something(arg)
function ("ff") dan function ("ff", "ff") akan bekerja.
Saya tidak dapat melihat skenario di mana fungsi isiterable () seperti milik Anda diperlukan. Ini bukan isinstance () yang merupakan gaya yang buruk tetapi situasi di mana Anda perlu menggunakan isinstance ().
sumber
type(a) == str
harus dihindari. Ini adalah praktik yang buruk karena tidak memperhitungkan jenis atau jenis yang serupastr
.type
tidak menaiki hierarki tipe, sedangkanisinstance
tidak, oleh karena itu lebih baik digunakanisinstance
.Untuk secara eksplisit memperluas peretasan Alex Martelli yang sangat baik
collections.py
dan menjawab beberapa pertanyaan di sekitarnya: Solusi yang berfungsi saat ini di python 3.6+ adalahimport 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
dan didemonstrasikan
>>> typs = ['string', iter(''), list(), dict(), tuple(), set()] >>> [isinstance(o, NonStringIterable) for o in typs] [False, True, True, True, True, True]
Jika Anda ingin menambahkan
iter('')
pengecualian, misalnya, ubah barisif issubclass(c, str): return False
menjadi
# `str_iterator` is just a shortcut for `type(iter(''))`* if issubclass(c, (str, cabc.str_iterator)): return False
mendapatkan
[False, False, True, True, True, True]
sumber