2016-04-21 7 views
2

Я знаю, что мы должны использовать метод setattr, когда мы находимся за пределами объекта. Тем не менее, у меня есть проблемы с вызовом setattr с ключом unicode, который позволяет мне напрямую использовать __setattr__.Python setattr vs __setattr__ UnicodeEncodeError

class MyObject(object): 
    def __init__(self): 
     self.__dict__["properties"] = dict() 
    def __setattr__(self, k, v): 
     self.properties[k] = v 
obj = MyObject() 

И я получаю следующее содержание: obj.properties

  • setattr(obj, u"é", u"à"): поднять UnicodeEncodeError
  • setattr(obj, "é", u"à"): {'\xc3\xa9': u'\xe0'}
  • obj.__setattr__(u"é", u"à"): {u'\xe9': u'\xe0'}

не знаю я ип что Python ведет себя с этими различиями

+0

Я использую Python 2.7.10 (по умолчанию, окт. 14 2015, 16:09:02) –

ответ

0

Python 2.7? Только идентификаторы Ascii. Это включает ваш код в 2) - аксиальный акцент, но не .1) - акцент unicode.

Unicode identifiers in Python?

3) включает в себя вы установив Юникода ключ в словаре. Юридические.

Обратите внимание, что __setattr__ is почти никогда не предназначено для использования, как вы это делаете. Он предназначен для установки атрибутов для объекта. Не перехватывайте это и не наполняйте их внутренним атрибутом dict. Я бы также избегал свойств, как имя, смешивая с свойствами в смысле get/Set.

Как правило, вы хотите использовать setattr, а не двойной символ подчеркивания. В отличие от вашего вступительного предложения.

Вы обычно также не используете call методы двойного подчеркивания, которые вы определяете, и базовый протокол данных Python вызывает их от вашего имени. Бит, как JavaBeans, получает/задает неявные вызовы (я думаю).

__setattr__ может быть сложным. Если вы не будете осторожны, он блокирует «настройку действий» неожиданными способами.

Вот глупый пример,

class Foo(object): 

    def __setattr__(self, attrname, value): 
     """ let's uppercase variables starting with k""" 

     if attrname.lower().startswith("k"): 
      self.__dict__[attrname.upper()] = value 

foo = Foo() 

foo.kilometer = 1000 
foo.meter = 1 

print "foo.KILOMETER:%s" % getattr(foo, "KILOMETER", "unknown") 
print "foo.meter:%s" % getattr(foo, "meter", "unknown") 
print "foo.METER:%s" % getattr(foo, "METER", "unknown") 

выход:

foo.KILOMETER:1000 
foo.meter:unknown 
foo.METER:unknown 

Вам необходимо иметь else после if:

 else: 
      self.__dict__[attrname] = value 

выход:

foo.KILOMETER:1000 
foo.meter:1 
foo.METER:unknown 

Последнее, если вы только начинаете, а unicode - это большое дело, я бы оценил Python 2 vs 3 - 3, имеет намного лучшую, унифицированную поддержку Unicode. Существует множество причин, по которым вам может понадобиться 2.7 или вместо 3, но unicode «толкает» к 3.

+0

Я, наконец, решил его, вызвав 'encode ('utf-8')' перед вызовом 'setattr'. В противном случае, в отношении последней точки, у меня есть следующее требование: иметь возможность доступа к объекту «toto» через 'obj.properties [" toto "]', а также непосредственно 'obj.toto'. Таким образом, перехват 'setattr' и' getattr' представляется единственным решением. –

+0

Francais? 'Toto' vs' foobar' дает это ;-) * Если * вам нужно только получить доступ через obj.toto для * reads *, то вы можете оставить setattr самостоятельно и вместо этого написать a \ __ getattr \ __, который возвращает obj.properties [ attrname]. Переопределение \ __ getattr \ __ является общим, \ __ setattr \ __ является более частным случаем и требует тщательного рассмотрения. У меня было бы что-то вроде моего глупого примера с именами переменных k и проверкой для ведущих '_' в именах атрибутов, чтобы разрешить обычные внутренние переменные. –

0

Python 2 не допускает идентификаторы Юникода:

>>> é = 3 
    File "<stdin>", line 1 
    é = 3 
    ^
SyntaxError: invalid syntax 

Предположительно это так настойчив в этом вопросе, что вы не можете работать вокруг него, как вы пытаетесь, потому что setattr проходит через некоторую обработку перед вызовом __setattr__. Вы можете показать это, вставив print в самом начале __setattr__: ничего не печатается, поэтому проблема не в вашем коде.