객체가 목록인지 튜플인지 확인하는 방법 (문자열은 아님)?
이것이 내가 일반적으로 입력이 있음을 확인하기 위하여 않는 것입니다 list
/ tuple
아니지만 - str
. 함수가 str
실수 로 객체를 전달하는 버그를 여러 번 발견했기 때문에 대상 함수 는 실제로 또는로 for x in lst
가정합니다 .lst
list
tuple
assert isinstance(lst, (list, tuple))
내 질문은 : 이것을 달성하는 더 좋은 방법이 있습니까?
파이썬 2에서만 (파이썬 3 아님) :
assert not isinstance(lst, basestring)
실제로 원하는 것입니다. 그렇지 않으면 목록처럼 작동하지만 list
또는의 하위 클래스가 아닌 많은 것들을 놓칠 수 tuple
있습니다.
파이썬에서 우리는 "오리 타이핑"을 사용하고 싶다는 것을 기억하십시오. 따라서 목록처럼 작동하는 모든 것을 목록으로 취급 할 수 있습니다. 따라서 목록의 유형을 확인하지 말고 목록처럼 작동하는지 확인하십시오.
그러나 문자열도 목록처럼 작동하며 종종 우리가 원하는 것이 아닙니다. 심지어 문제 일 때도 있습니다! 따라서 문자열을 명시 적으로 확인한 다음 오리 타이핑을 사용하십시오.
다음은 내가 재미있게 작성한 기능입니다. repr()
꺾쇠 괄호 ( '<', '>')로 시퀀스를 인쇄 하는 특수 버전입니다 .
def srepr(arg):
if isinstance(arg, basestring): # Python 3: isinstance(arg, str)
return repr(arg)
try:
return '<' + ", ".join(srepr(x) for x in arg) + '>'
except TypeError: # catch when for loop fails
return repr(arg) # not a sequence so just return repr
이것은 전체적으로 깨끗하고 우아합니다. 그러나 그 isinstance()
검사는 무엇 입니까? 그것은 일종의 해킹입니다. 그러나 필수입니다.
이 함수는 목록처럼 작동하는 모든 것을 재귀 적으로 호출합니다. 문자열을 특수하게 처리하지 않으면 목록처럼 취급되어 한 번에 한 문자 씩 분할됩니다. 그러나 재귀 호출은 각 문자를 목록으로 취급하려고 시도하며 작동합니다! 한 문자 문자열조차도 목록으로 작동합니다! 이 함수는 스택 오버플로가 발생할 때까지 계속 재귀 적으로 호출합니다.
수행 할 작업을 세분화하는 각 재귀 호출에 의존하는 이와 같은 함수는 문자열을 특수 사례로 묶어야합니다. 한 문자열 이하의 문자열, 심지어 한 문자열 이하의 문자열을 분해 할 수 없기 때문에 -문자열은 목록처럼 작동합니다.
참고 : try
/ except
는 의도를 표현하는 가장 깨끗한 방법입니다. 그러나이 코드가 어떻게 시간이 중요하다면, 우리는이 코드가 arg
시퀀스 인지 확인하기 위해 일종의 테스트로 대체하고 싶을 것입니다 . 유형을 테스트하는 대신 동작을 테스트해야합니다. .strip()
메서드 가 있으면 문자열이므로 시퀀스로 간주하지 마십시오. 그렇지 않으면 색인 가능하거나 반복 가능하면 시퀀스입니다.
def is_sequence(arg):
return (not hasattr(arg, "strip") and
hasattr(arg, "__getitem__") or
hasattr(arg, "__iter__"))
def srepr(arg):
if is_sequence(arg):
return '<' + ", ".join(srepr(x) for x in arg) + '>'
return repr(arg)
편집 : 원래 위의 내용을 확인 __getslice__()
했지만 collections
모듈 설명서에서 흥미로운 방법은 다음과 같습니다 __getitem__()
. 이것은 의미가 있습니다. 이것이 객체를 색인하는 방법입니다. 그보다 더 근본적인 것처럼 보이 __getslice__()
므로 위의 내용을 변경했습니다.
H = "Hello"
if type(H) is list or type(H) is tuple:
## Do Something.
else
## Do Something.
파이썬 3의 경우 :
import collections.abc
if isinstance(obj, collections.abc.Sequence) and not isinstance(obj, str):
print("obj is a sequence (list, tuple, etc) but not a string")
버전 3.3으로 변경 : Collections 추상베이스 클래스를 collections.abc 모듈로 이동했습니다. 이전 버전과의 호환성을 위해 버전 3.8까지 작동을 멈출 때까지이 모듈에서 계속 볼 수 있습니다.
파이썬 2의 경우 :
import collections
if isinstance(obj, collections.Sequence) and not isinstance(obj, basestring):
print "obj is a sequence (list, tuple, etc) but not a string or unicode"
PHP 풍미를 가진 파이썬 :
def is_array(var):
return isinstance(var, (list, tuple))
일반적으로 객체를 반복하는 함수는 튜플 및 목록뿐만 아니라 문자열에서도 작동한다는 사실은 버그보다 더 많은 기능입니다. 당신은 확실히 할 수 사용 isinstance
하거나 입력 오리 인수를 확인하기 위해, 그런데 왜 당신해야?
그것은 수사적인 질문처럼 들리지만 그렇지 않습니다. "왜 인수 유형을 확인해야합니까?" 아마도 인식 된 문제가 아니라 실제 문제에 대한 해결책을 제안 할 것입니다. 문자열이 함수에 전달 될 때 왜 버그입니까? 또한 : 문자열 이이 함수에 전달 될 때 버그 인 경우 목록이 아닌 튜플 iterable이 아닌 다른 것이 전달됩니까? 그 이유는 무엇?
질문에 대한 가장 일반적인 대답은 글을 쓰는 개발자 f("abc")
가 작성한 것처럼 함수가 작동하기를 기대하고 있을 가능성이 있다고 생각합니다 f(["abc"])
. 문자열의 문자를 반복하는 유스 케이스를 지원하는 것보다 개발자를 스스로 보호하는 것이 더 합리적인 상황 일 수 있습니다. 그러나 나는 그것에 대해 먼저 길고 열심히 생각할 것입니다.
가독성과 모범 사례를 위해 다음을 시도하십시오.
파이썬 2
import types
if isinstance(lst, types.ListType) or isinstance(lst, types.TupleType):
# Do something
파이썬 3
import typing
if isinstance(lst, typing.List) or isinstance(lst, typing.Tuple):
# Do something
도움이 되길 바랍니다.
str
객체는없는 __iter__
속성을
>>> hasattr('', '__iter__')
False
그래서 당신은 확인을 할 수 있습니다
assert hasattr(x, '__iter__')
그리고 이것은 AssertionError
반복 불가능한 다른 객체에도 좋을 것 입니다.
편집 : Tim이 주석에서 언급했듯이 이것은 3.x가 아닌 python 2.x에서만 작동합니다.
이것은 OP에 직접 대답하기위한 것이 아니라 관련 아이디어를 공유하고 싶었습니다.
나는 위의 @ steveha 답변에 매우 관심이 있었고 오리 타이핑이 깨지는 것처럼 보이는 예를 보였습니다. 그러나 두 번째 생각으로, 그의 예는 오리 타이핑이 준수하기 어렵다는 것을 제안하지만, 특별한 취급 이 필요 하다는 것은 아닙니다str
.
결국, 비 str
유형 (예를 들어, 복잡한 재귀 구조를 유지하는 사용자 정의 유형)은 @steveha srepr
함수가 무한 재귀를 유발할 수 있습니다 . 이것은 다소 가능성이 없지만이 가능성을 무시할 수는 없습니다. 따라서, 특별-케이스 대신 str
에 srepr
, 우리는 우리가 원하는 것을 명확히해야 srepr
할 때 무한 재귀 결과 할 수 있습니다.
하나의 합리적인 접근 방식은 간단에서 재귀를 파괴하는 것입니다 것처럼 보일 수 있습니다 srepr
순간 list(arg) == [arg]
. 이 사실에 완전히의 문제를 해결할 str
어떤 않고 isinstance
.
그러나 매우 복잡한 재귀 구조는 list(arg) == [arg]
결코 발생하지 않는 무한 루프를 일으킬 수 있습니다 . 따라서 위의 검사는 유용하지만 충분하지 않습니다. 재귀 깊이에 대한 하드 제한과 같은 것이 필요합니다.
내 요점은 임의의 인수 유형을 처리하려는 경우 str
오리 이론을 통해 처리하는 것이 일반적인 이론 유형보다 더 일반적인 유형을 처리하는 것보다 훨씬 쉽다는 것입니다. 따라서 str
인스턴스 를 제외 해야한다고 생각하면 인수가 명시 적으로 지정하는 몇 가지 유형 중 하나의 인스턴스 여야합니다.
tensorflow에서 is_sequence 라는 함수 를 찾았습니다 .
def is_sequence(seq):
"""Returns a true if its input is a collections.Sequence (except strings).
Args:
seq: an input sequence.
Returns:
True if the sequence is a not a string and is a collections.Sequence.
"""
return (isinstance(seq, collections.Sequence)
and not isinstance(seq, six.string_types))
그리고 귀하의 요구를 충족시키는 것으로 확인되었습니다.
내 테스트 케이스 에서이 작업을 수행합니다.
def assertIsIterable(self, item):
#add types here you don't want to mistake as iterables
if isinstance(item, basestring):
raise AssertionError("type %s is not iterable" % type(item))
#Fake an iteration.
try:
for x in item:
break;
except TypeError:
raise AssertionError("type %s is not iterable" % type(item))
발전기에서 테스트하지 않은 상태에서 발전기를 통과하면 다음 '수율'에 남아있어 다운 스트림을 망칠 수 있습니다. 그러나 다시, 이것은 '유니트 테스트'입니다
가장 간단한 방법은 ...을 사용 any
하고isinstance
>>> console_routers = 'x'
>>> any([isinstance(console_routers, list), isinstance(console_routers, tuple)])
False
>>>
>>> console_routers = ('x',)
>>> any([isinstance(console_routers, list), isinstance(console_routers, tuple)])
True
>>> console_routers = list('x',)
>>> any([isinstance(console_routers, list), isinstance(console_routers, tuple)])
True
「오리 타이핑」방식으로 어떻습니까
try:
lst = lst + []
except TypeError:
#it's not a list
또는
try:
lst = lst + ()
except TypeError:
#it's not a tuple
각기. 이것은 isinstance
/ hasattr
내부 검사를 피합니다 .
당신은 또한 그 반대로 확인할 수 있습니다 :
try:
lst = lst + ''
except TypeError:
#it's not (base)string
모든 변형은 실제로 변수의 내용을 변경하지 않지만 재 할당을 암시합니다. 일부 환경에서는 이것이 바람직하지 않을 수 있습니다.
흥미롭게도, "in place"할당 으로 리스트가 ( tuple이 아닌) 어떤 경우에도 발생 +=
하지 TypeError
않을 것 입니다. 이것이 과제가 이런 식으로 수행되는 이유입니다. 어쩌면 누군가가 왜 그런지 밝힐 수 있습니다.lst
그냥하세요
if type(lst) in (list, tuple):
# Do stuff
파이썬 3에는 다음이 있습니다.
from typing import List
def isit(value):
return isinstance(value, List)
isit([1, 2, 3]) # True
isit("test") # False
isit({"Hello": "Mars"}) # False
isit((1, 2)) # False
따라서 List와 Tuple을 모두 확인하려면 다음과 같습니다.
from typing import List, Tuple
def isit(value):
return isinstance(value, List) or isinstance(value, Tuple)
assert (type(lst) == list) | (type(lst) == tuple), "Not a valid lst type, cannot be string"
나는 이것을하는 경향이 있습니다 (내가 정말로 정말로해야한다면) :
for i in some_var:
if type(i) == type(list()):
#do something with a list
elif type(i) == type(tuple()):
#do something with a tuple
elif type(i) == type(str()):
#here's your string
'development' 카테고리의 다른 글
여러 테이블에서 SQL 쿼리 반환 데이터 (0) | 2020.02.19 |
---|---|
레일에서 문자열과 텍스트의 차이점은 무엇입니까? (0) | 2020.02.19 |
mysqldump를 사용하지 않고 데이터베이스 복사 / 복제 (0) | 2020.02.19 |
테이블을 잠그지 않고 MySQLDump 실행 (0) | 2020.02.19 |
로컬 변경을 무시하면서 힘내? (0) | 2020.02.19 |