2010-07-16 5 views
4

В обращении немного SQL форматирования, я был поражен, чтобы найти я мог струнные цепи форматтеры:Прикованный строка форматирования в Python

def get_sql(table, limit=True): 
    sql = "select report_date from %s" 
    if limit: 
     result = "%s limit 1" % sql % table 
    else: 
     result = sql % table 
    return result 

Является ли это законно? Любая причина не делать этого?

ответ

4

Это имеет смысл, что это работает, потому что утверждение, как это:

'some value goes here %s' % value

На самом деле возвращает строку. Это, вероятно, немного более логичным, чтобы просмотреть это следующим образом:

result = ("%s limit 1" % sql) % table

Там нет ничего явно плохого делать это, но цепные операторы могут привести к проблемам с выяснить, где пришел ошибка с.

Так, например, это работает отлично:

>>> sql = 'a value of %s' 
>>> x = 'some string %s with stuff' 
>>> y = 'VALUE' 
>>> x % sql % y 
'some string a value of VALUE with stuff' 

Но если произошла ошибка форматирования там (я понимаю, что этот пример патологический, но он получает через точку):

>>> sql = 'a value of %d' 
>>> x = 'some string %d with stuff' 
>>> y = 123  
>>> x % sql % y 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
TypeError: %d format: a number is required, not str 

Непонятно, какая из %d вызывает вашу ошибку. По этой причине я бы разделил его и просто использовал один форматир % на строку, если это возможно, потому что тогда трассировка сможет указать вам точно, в какой строке и с какой форматирующей проблемой возникла проблема.

Для справки, сделав это одним форматированием на строку, вы также облегчите жизнь другим, кто должен прочитать ваш код и попытаться выяснить, что происходит.

3

Это совершенно законно.

«single argument» форма строки форматировщиком действительно особый случай - для нескольких элементов кортежа обычно используется, и что привело бы к более очевидный пример того, почему это нормально

result = "%s limit 1" % (sql % (table,),) 

Это^была первоначально написана, чтобы побудить вопросника о том, что поддержка нескольких форматов является законной языковой функцией, но, как замечает Нас Банов, она читает, как будто я пытаюсь объяснить, как это работает (не помогло прикручивать код). Он не строит строку справа налево, как это предполагает, что это возможно, но HAS для ее создания (быть ассоциативным) слева направо. Оператор должен взять строку слева и вернуть один, но может взять нестрочную (кортеж) справа. Так как вы не можете использовать% на пару кортежей она не может работать в обратном направлении

>>> "%s %f %s" % ("%d", 0.1, "%d %d") % (1,2,3) 
'1 0.100000 2 3' 

Это, однако, может привести к сложному/грязному коду, поэтому лично я хотел бы использовать его очень экономно.

Вы могли бы работать ваш пример, как это:

def get_sql(table, limit=True): 
    sql = "select report_date from" 
    strlimit = "" 
    if limit: 
     strlimit = "limit 1" 
    return "%s %s"%(sql, strlimit) 
+0

'-': Неправильное объяснение, это не имеет ничего общего с несколькими аргументами, и ассоциативность неверна. Попробуйте ''% ss limit 1 "% '%'% 'BOO'', чтобы увидеть, что я имею в виду, это выражение работает, всякий раз, когда мы помещаем скобки, как вы предлагаете,' '% ss limit 1"% ('% '% ('BOO')) 'breaks/error. Кроме того, ваше предлагаемое исправление не лучше оригинала –

+0

Я думаю, вы неправильно поняли. Я не предполагал, что это имело какое-либо отношение к сингулярности или множественности аргументов, просто если бы вы перешли от использования синглов к кортежам, стало бы более ясно, что это законная вещь. Ваш встречный пример неверен - ('%'% ('BOO')) недействителен. '"% s limit 1 "% '% s'% 'BOO'' против' '% s limit 1"% ('% s '% (' BOO ')) '. Оба работают. Обратите внимание, что вопрос не содержит формат '% ss', который нужен вашему примеру. Это не «предлагаемое исправление» - в исходном коде нет ничего плохого, это всего лишь предлагаемое изменение. – pycruft

+0

Похоже, вы этого не понимаете. (A) Помещение скобок так, как вы делали, меняет порядок операций **. Когда у вас есть выражение типа 'A% B% C', оно выполняется как' (A% B)% C', а не как 'A% (B% C)', так как ваш первый набор parens заставляет его , (B) Кроме того, ваше понимание того, что put() вокруг выражения делает кортеж из него неправильным, - это не так. Для принудительного кортежа вам нужно либо явно использовать конструктор 'tuple()', либо использовать «глупую запятую», например, так: «% s limit 1»% (sql% (table,))) - это создаст кортежи, но ваш пример по-прежнему будет неправильным из-за (A) –

0

я бы написать так:

def get_sql(table,limit=True): 
    sql = "select report_date from %s"%table 
    if limit: sql += " limit 1" 
    return sql 

PS.Код, который вы опубликовали, работает следующим образом:

In [49]: "%s limit 1" % sql 
Out[49]: 'select report_date from %s limit 1' 

In [50]: "%s limit 1" % sql % 'table' 
Out[50]: 'select report_date from table limit 1' 

Уверенный, вы можете это сделать, но я не думаю, что это особенно ясно.

+0

LOL, зачем вам нужно использовать% форматирования? ;-) (намного лучше, чем оригинал) –

2

Почему да, это возможно, чтобы связать форматирование строки таким образом, даже если это первый раз, когда я вижу, что он используется (и ужасным образом, заметьте)!

Причина заключается в том, что операторы одного и того же типа group left to right (есть «левый ассоциативность» - с примечательными исключениями из экспоненциации ** и сравнений a<b<c).

Так точно так же, что

>>> 1 - 2 - 3 # equals to (1-2)-3 
-4 
>>> 16/4/2 # equals to (16 /4) /2, NOT 16/(4/2) 
2 

так делает s1 % s2 % s3 равным (s1 % s2) % s3

Ох и, кстати, это не имеет значения, если s1, s2 и s3 строки или числа - компилятор не знает, что во время компиляции и только во время выполнения будет определено, что % означает «остаток от деления» (если s1 - число) или форматирование строки (если s1 - строка).

Смежные вопросы