IT TIP

변수가 반복 가능하지만 문자열이 아니라는 것을 알려주는 방법

itqueen 2020. 10. 24. 12:11
반응형

변수가 반복 가능하지만 문자열이 아니라는 것을 알려주는 방법


단일 항목 또는 이중 항목이 될 수있는 인수를받는 함수가 있습니다.

def iterable(arg)
    if #arg is an iterable:
        print "yes"
    else:
        print "no"

그래서:

>>> iterable (( "f", "f"))

>>> 반복 가능 ([ "f", "f"])

>>> iterable ( "ff")
아니

문제는 문자열이 기술적으로 반복 가능하므로 시도 할 때 ValueError를 잡을 수 없다는 것 arg[1]입니다. isinstance ()는 좋은 습관이 아니기 때문에 사용하고 싶지 않습니다.


isinstance 사용 (왜 나쁜 습관인지 모르겠습니다)

import types
if not isinstance(arg, types.StringTypes):

StringTypes 사용에 유의하십시오. 모호한 유형의 문자열을 잊지 않도록합니다.

장점은 파생 된 문자열 클래스에서도 작동합니다.

class MyString(str):
    pass

isinstance(MyString("  "), types.StringTypes) # true

또한이 이전 질문을 살펴볼 수도 있습니다 .

건배.


NB : 행동이 파이썬 3으로 변경 StringTypes하고 basestring더 이상 정의되지 않습니다. 필요에 따라, 당신은에 장착 할 수 isinstance에 의해 str, 또는 부분 집합의 튜플 (str, bytes, unicode)사이 썬 사용자를위한, 예. 으로 @Theron Luhn는 했나요, 당신은 또한 사용할 수 있습니다 six.


2017 년 현재 모든 버전의 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

추상 기본 클래스가 도입 된 Python 2.6 이후 isinstance(구체적인 클래스가 아닌 ABC에서 사용됨)는 이제 완벽하게 허용되는 것으로 간주됩니다. 구체적으로 특별히:

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

This is an exact copy (changing only the class name) of Iterable as defined in _abcoll.py (an implementation detail of collections.py)... the reason this works as you wish, while collections.Iterable doesn't, is that the latter goes the extra mile to ensure strings are considered iterable, by calling Iterable.register(str) explicitly just after this class statement.

Of course it's easy to augment __subclasshook__ by returning False before the any call for other classes you want to specifically exclude from your definition.

In any case, after you have imported this new module as myiter, isinstance('ciao', myiter.NonStringIterable) will be False, and isinstance([1,2,3], myiter.NonStringIterable)will be True, just as you request -- and in Python 2.6 and later this is considered the proper way to embody such checks... define an abstract base class and check isinstance on it.


I realise this is an old post but thought it was worth adding my approach for Internet posterity. The function below seems to work for me under most circumstances with both Python 2 and 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

This checks for a non-string iterable by (mis)using the built-in hasattr which will raise a TypeError when its second argument is not a string or unicode string.


By combining previous replies, I'm using:

import types
import collections

#[...]

if isinstance(var, types.StringTypes ) \
    or not isinstance(var, collections.Iterable):

#[Do stuff...]

Not 100% fools proof, but if an object is not an iterable you still can let it pass and fall back to duck typing.


Edit: Python3

types.StringTypes == (str, unicode). The Phython3 equivalent is:

if isinstance(var, str ) \
    or not isinstance(var, collections.Iterable):

huh, don't get it... what's wrong with going

hasattr( x, '__iter__' )

?

... NB elgehelge puts this in a comment here, saying "look at my more detailed answer" but I couldn't find his/her detailed answer

later

In view of David Charles' comment about Python3, what about:

hasattr(x, '__iter__') and not isinstance(x, (str, bytes))

? Apparently "basestring" is no longer a type in Python3:

https://docs.python.org/3.0/whatsnew/3.0.html

The builtin basestring abstract type was removed. Use str instead. The str and bytes types don’t have functionality enough in common to warrant a shared base class.

As you point out correctly, a single string is a character sequence.

So the thing you really want to do is to find out what kind of sequence arg is by using isinstance or type(a)==str.

If you want to realize a function that takes a variable amount of parameters, you should do it like this:

def function(*args):
    # args is a tuple
    for arg in args:
        do_something(arg)

function("ff") and function("ff", "ff") will work.

I can't see a scenario where an isiterable() function like yours is needed. It isn't isinstance() that is bad style but situations where you need to use isinstance().

참고URL : https://stackoverflow.com/questions/1055360/how-to-tell-a-variable-is-iterable-but-not-a-string

반응형