Python: динамическое добавление атрибутов в класс нового стиля / obj

#python #attributes #metaprogramming

#python #атрибуты #метапрограммирование

Вопрос:

Могу ли я динамически добавлять атрибуты к экземплярам класса нового стиля (того, который является производным от object )?

Подробные сведения:

Я работаю с экземпляром sqlite3.Подключение. Простое расширение класса — это не вариант, потому что я не получаю экземпляр, вызывая конструктор; я получаю его, вызывая sqlite3.connect() .

Создание оболочки не сильно экономит объем кода, который я пишу.

Python 2.7.1

Редактировать

Правильные ответы на все. Но я все еще не достигаю своей цели; экземпляры sqlite3.Панель подключения my пытается установить атрибуты следующими способами (как и экземпляры object самого себя). Я всегда получаю ошибку AttributeError:

 > conn = sqlite3.connect([filepath])
> conn.a = 'foo'
Traceback (most recent call last):
  File "<pyshell#2>", line 1, in <module>
    conn.a = 'foo'
AttributeError: 'object' object has no attribute 'a'
> conn.__setattr__('a','foo')
Traceback (most recent call last):
  File "<pyshell#2>", line 1, in <module>
    conn.__setattr__('a','foo')
AttributeError: 'object' object has no attribute 'a'
  

Помогите?

Комментарии:

1. Вы можете изучить фактический dict , чтобы узнать, что использует sqlite3. Но это может быть подпрограммой C, и в этом случае вам придется как-то ее обернуть.

2. 1. Даже object.__setattr__(conn, 'a', 'foo') вызывает ошибку AttributeError. Интересный вопрос!

Ответ №1:

Да, если только класс не использует __slots__ или не запрещает запись атрибута путем переопределения __setattr__ , или внутреннего класса Python, или класса Python, реализованного изначально (обычно на C).

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

 class AttrWrapper(object):
  def __init__(self, wrapped):
    self._wrapped = wrapped
  def __getattr__(self, n):
    return getattr(self._wrapped, n)
conn = AttrWrapper(sqlite3.connect(filepath))
  

Комментарии:

1. Теперь я вижу, что вы правы, но это не работает для экземпляров sqlite3. Подключение или object само по себе. (См. Редактирование выше.) Любая помощь?

2. @JellicleCat Оба относятся к «внутреннему классу Python». Я расширил ответ с помощью оболочки. Вы упомянули об использовании одного из них в вопросе; но я не уверен, почему это не решило бы проблему. Если эта оболочка не работает, пожалуйста, дополните свой вопрос (или комментарий здесь) сценарием, в котором он завершается неудачей.

Ответ №2:

Простой эксперимент:

 In []: class Tst(object): pass
   ..: 
In []: t= Tst()
In []: t.attr= 'is this valid?'
In []: t.attr
Out[]: 'is this valid?'
  

Итак, действительно, кажется, это возможно сделать.

Обновить:
Но из документации: SQLite — это библиотека C, которая … поэтому кажется, что вам действительно нужно ее обернуть.

Комментарии:

1. Теперь я вижу, что вы правы, но это не работает для экземпляров sqlite3. Подключение или object само по себе. (См. Редактирование выше.) Любая помощь?

Ответ №3:

     conn.a = 'foo',
  

или любое динамическое присвоение допустимо, если conn является

     <type 'classobj'>.
  

Такие вещи, как:

     c=object() 
    c.e=1
  

вызовет ошибку атрибута. С другой стороны: Python позволяет выполнять фантастическое программирование в метаклассах:

     >>>from new import classobj
    >>>Foo2 = classobj('Foo2',(Foo,),{'bar':lambda self:'bar'})
    >>>Foo2().bar()
    >>>'bar'
    >>>Foo2().say_foo()
    >>>foo