Помните, что в Python мы хотим использовать "типизацию утки". Таким образом, все, что действует как список, может рассматриваться как список. Поэтому не проверяйте тип списка, просто посмотрите, действует ли он как список.
Но строки тоже действуют как список, и часто это не то, что мы хотим. Есть моменты, когда это даже проблема! Так что, проверяйте явно строку, но затем используйте утку.
Вот функция, которую я написал для удовольствия. Это специальная версия 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__()
, поэтому я изменил вышесказанное.