Python предпочитает неназначенную локальную функцию встроенной функции - PullRequest
2 голосов
/ 08 июля 2011

Следующий скрипт Python хорошо работает с Python 2.3 и Python 2.4 (которые не имеют встроенного определения all():

#! /usr/bin/env python
# vim: set fileencoding=utf-8
# (c) Uwe Kleine-König
# GPLv2

import locale
import sys

f = file(sys.argv[1])
data = f.read()

def len_utf8_char(data):
    if not 'all' in dir(__builtins__):
        def all(seq):
            for i in seq:
                if not i:
                    return False
            return True

    def check_cont(num):
        if all(map(lambda c: ord(c) >= 0x80 and ord(c) <= 0xbf, data[1:num])):
            return num
        else:
            return -1

    if ord(data[0]) < 128:
        # ASCII char
        return 1
    elif ord(data[0]) & 0xe0 == 0xc0:
        return check_cont(2)
    elif ord(data[0]) & 0xf0 == 0xe0:
        return check_cont(3)
    elif ord(data[0]) & 0xf8 == 0xf0:
        return check_cont(4)
    elif ord(data[0]) & 0xfc == 0xf8:
        return check_cont(5)
    elif ord(data[0]) & 0xfe == 0xfc:
        return check_cont(6)

i = 0
maxl = 0
while i < len(data):
    l = len_utf8_char(data[i:])
    if l < 0:
        prefenc = locale.getpreferredencoding()
        if prefenc not in ('UTF-8', 'ANSI_X3.4-1968'):
            print prefenc
        else:
            print 'ISO-8859-1'
        sys.exit(0)

    if maxl < l:
        maxl = l
    i += l

if maxl > 1:
    print 'UTF-8'
else:
    print 'ANSI_X3.4-1968'

Теперь с Python 2.5 и более поздними версиями это не работает следующим образом:

$ python2.5 guess-charmap guess-charmap
Traceback (most recent call last):
  File "guess-charmap", line 43, in <module>
    l = len_utf8_char(data[i:])
  File "guess-charmap", line 30, in len_utf8_char
    return check_cont(2)
  File "guess-charmap", line 21, in check_cont
    if all(map(lambda c: ord(c) >= 0x80 and ord(c) <= 0xbf, data[1:num])):
NameError: free variable 'all' referenced before assignment in enclosing scope

Удаление определения совместимости всех исправляет проблему для Python 2.5+. Интересно, почему Python не выбирает встроенный all() в этом случае. Кто-нибудь может объяснить?

Ответы [ 4 ]

5 голосов
/ 08 июля 2011

Когда Python анализирует тело функции, он ищет имена переменных, которые используются в присваиваниях. Все такие переменные предполагаются локальными, если только не используется объявление переменной global.

def all присваивает значение имени переменной all. Несмотря на то, что присвоение находится внутри if-block, all рассматривается как локальная переменная во всех случаях (независимо от того, выполняется ли if-block позже).

Когда блок if не выполняется, all становится несвязанной локальной переменной, вызывая тем самым ошибку NameError.

Если вы переместите блок if not 'all' ... за пределы def len_utf8_char, то Вы избежите этой проблемы.

3 голосов
/ 08 июля 2011

По той же причине, что и с переменными;компилятор пометил его как локальный для функции и поэтому ожидает, что он будет локальным.Если вы хотите решить эту проблему, просто наберите all = __builtins__.all в предложении else.

2 голосов
/ 08 июля 2011

Вы можете поместить определение all на уровне модуля следующим образом:

try:
    all
except NameError:
    def all(seq):
        for i in seq:
            if not i:
                return False
        return True
0 голосов
/ 08 июля 2011

Потому что, когда вы определяете свою функцию после all (), вы все еще находитесь внутри локальной области видимости. Почему у вас так много определений функций внутри функции? Зачем вообще определять all ()? И почему бы не использовать дикт для этого

   if ord(data[0]) < 128:
        # ASCII char
        return 1
    elif ord(data[0]) & 0xe0 == 0xc0:
        return check_cont(2)
    elif ord(data[0]) & 0xf0 == 0xe0:
        return check_cont(3)
    elif ord(data[0]) & 0xf8 == 0xf0:
        return check_cont(4)
    elif ord(data[0]) & 0xfc == 0xf8:
        return check_cont(5)
    elif ord(data[0]) & 0xfe == 0xfc:
        return check_cont(6)

Фактически, я бы попросил переписать этот код, он сложный и раздражающий.

...