Цель состоит в том, чтобы создать фиктивный класс, который ведет себя как набор результатов db.
Так, например, если запрос к базе данных возвращается, используя выражение dict {'ab':100, 'cd':200}
, то я хотел бы видеть:
>>> dummy.ab
100
Сначала я подумал, может быть, я мог бы сделать это так:
ks = ['ab', 'cd']
vs = [12, 34]
class C(dict):
def __init__(self, ks, vs):
for i, k in enumerate(ks):
self[k] = vs[i]
setattr(self, k, property(lambda x: vs[i], self.fn_readyonly))
def fn_readonly(self, v)
raise "It is ready only"
if __name__ == "__main__":
c = C(ks, vs)
print c.ab
но c.ab
вместо этого возвращает объект свойства.
Замена setattr
строки k = property(lambda x: vs[i])
бесполезна.
Итак, как правильно создать свойство экземпляра во время выполнения?
PS Мне известна альтернатива, представленная в разделе Как используется __getattribute__
метод?
python
properties
runtime
monkeypatching
Энтони Конг
источник
источник
:
и__init__
ссылкиself.fn_readyonly
.Ответы:
Полагаю, мне следует расширить этот ответ, теперь, когда я стал старше и мудрее и знаю, что происходит. Лучше поздно, чем никогда.
Вы можете добавить свойство к классу динамически. Но в этом-то и суть: вы должны добавить это в класс .
На
property
самом деле это простая реализация вещи, называемой дескриптором . Это объект, который обеспечивает пользовательскую обработку для данного атрибута в данном классе . Вроде как способ отделить огромноеif
дерево__getattribute__
.Когда я прошу
foo.b
в приведенном выше примере, Python видит , чтоb
определенный на классе реализует протокол дескриптора -Как просто означает , что это объект с__get__
,__set__
или__delete__
методом. Дескриптор берет на себя ответственность за обработку этого атрибута, поэтому вызовы PythonFoo.b.__get__(foo, Foo)
и возвращаемое значение возвращаются вам как значение атрибута. В случаеproperty
, каждый из этих методов просто называетfget
,fset
илиfdel
вы прошли вproperty
конструктор.Дескрипторы - это действительно способ Python раскрыть всю реализацию OO. На самом деле, есть еще один тип дескриптора, даже более распространенный, чем
property
.Скромный метод - это просто другой вид дескриптора. В
__get__
качестве первого аргумента указывается на вызывающий экземпляр; по сути, он делает это:В любом случае, я подозреваю, что именно поэтому дескрипторы работают только с классами: они являются формализацией того, что обеспечивает классы в первую очередь. Они даже являются исключением из правила: очевидно, вы можете назначать дескрипторы классу, а сами классы являются экземплярами
type
! На самом деле, пытаясь прочитатьFoo.b
еще звонкиproperty.__get__
; для дескрипторов идиоматично возвращать себя, когда к ним обращаются как к атрибутам класса.Я думаю, это очень круто, что практически вся ОО-система Python может быть выражена в Python. :)
О, и я недавно написал многословное сообщение в блоге о дескрипторах , если вам интересно.
источник
setattr (Foo, 'name', property (func))
Итак, что вы хотите - это словарь, в котором вы можете написать a ['b'] как ab?
Это легко:
источник
d.items = 1
,d.items
возвращается<built-in method items of atdict object at ...>
. Вы все еще можете сделатьd["items"]
или использовать__getattribute__
вместо__getattr__
, но это мешает использовать большинство методов dict.Кажется, вы могли бы решить эту проблему намного проще с помощью a
namedtuple
, так как вы знаете весь список полей заранее.Если вам абсолютно необходимо написать собственный установщик, вам придется выполнять метапрограммирование на уровне класса;
property()
не работает на экземплярах.источник
namedtuple
заслуживает награды за то, что он сделал его гладким и элегантным, чтобы быть верным объектно-ориентированным принципам.property()
? ни в одном ответе нет ничего специфичного только для чтения.Вам не нужно использовать свойство для этого. Просто переопределите,
__setattr__
чтобы сделать их только для чтения.Тад.
источник
Допустим, у вас есть объект, к которому вы хотите добавить свойство. Как правило, я хочу использовать свойства, когда мне нужно начать управлять доступом к атрибуту в коде, который используется в нисходящем направлении, чтобы я мог поддерживать согласованный API. Теперь я обычно добавляю их в исходный код, в котором определен объект, но давайте предположим, что у вас нет такого доступа, или вам нужно действительно динамически выбирать свои функции программно.
Создать класс
Используя пример, основанный на документации для
property
, давайте создадим класс объекта со скрытым атрибутом и создадим его экземпляр:В Python мы ожидаем, что есть один очевидный способ сделать что-то. Однако в этом случае я собираюсь показать два способа: с обозначением декоратора и без. Во-первых, без обозначения декоратора. Это может быть более полезно для динамического назначения методов получения, установки или удаления.
Динамический (он же Monkey Patching)
Давайте создадим некоторые для нашего класса:
И теперь мы назначаем их на собственность. Обратите внимание, что мы могли бы выбрать наши функции программно здесь, отвечая на динамический вопрос:
И использование:
Декораторы
Мы могли бы сделать то же самое, что мы делали выше с нотацией декоратора, но в этом случае мы должны назвать методы с одинаковым именем (и я бы рекомендовал оставить его таким же, как у атрибута), так что программное назначение не так тривиально, как он использует вышеуказанный метод:
И присвойте объекту свойства с предоставленными ему установщиками и удалителями класс:
И использование:
источник
Я задал похожий вопрос в этом сообщении о переполнении стека, чтобы создать фабрику классов, которая создавала простые типы. Результатом стал ответ, который имел рабочую версию фабрики классов. Вот фрагмент ответа:
Вы можете использовать некоторые варианты этого для создания значений по умолчанию, которые являются вашей целью (в этом вопросе также есть ответ, который имеет дело с этим).
источник
Не уверен, что я полностью понимаю вопрос, но вы можете изменить свойства экземпляра во время выполнения с помощью встроенного в
__dict__
ваш класс:источник
self.__dict__[key] = value
Для тех, кто прибывает из поисковых систем, вот две вещи, которые я искал, говоря о динамических свойствах:
__dict__
хорошо, если вы хотите поставить динамически созданные свойства.__getattr__
хорошо делать что-то только тогда, когда нужно это значение, например, запрашивать базу данных. Комбинация set / get полезна для упрощения доступа к данным, хранящимся в классе (как в примере выше).Если вам нужно только одно динамическое свойство, взгляните на встроенную функцию property () .
источник
Вы не можете добавить новый
property()
экземпляр во время выполнения, потому что свойства являются дескрипторами данных. Вместо этого вы должны динамически создать новый класс или перегрузить__getattribute__
для обработки дескрипторов данных в экземплярах.источник
Лучший способ достичь - это определить
__slots__
. Таким образом, ваши экземпляры не могут иметь новые атрибуты.Это печатает
12
Это дает:
AttributeError: 'C' object has no attribute 'ab'
источник
Еще один пример того, как добиться желаемого эффекта
Так что теперь мы можем делать такие вещи, как:
источник
Вот решение, которое:
После того, как класс был определен, вы просто делаете это для динамического добавления свойства:
Вот полный пример, протестированный в Python 3:
источник
Вы можете использовать следующий код для обновления атрибутов класса, используя объект словаря:
источник
Это немного отличается от того, что хотел ОП, но я ломал голову, пока не нашел рабочее решение, поэтому я выкладываю сюда для следующего парня / гал
Мне нужен был способ указать динамические сеттеры и геттеры.
Я знаю свои поля раньше времени, поэтому я собираюсь создать свои свойства. ПРИМЕЧАНИЕ: вы не можете сделать этот экземпляр PER, эти свойства будут существовать в классе !!!
Давайте проверим все это сейчас ..
Это сбивает с толку? Да, извините, я не смог придумать сколько-нибудь значимых примеров из реального мира. Кроме того, это не для слабонервных.
источник
Это похоже на работу (но смотри ниже):
Если вам нужно более сложное поведение, не стесняйтесь редактировать свой ответ.
редактировать
Следующее, вероятно, будет более эффективным для памяти для больших наборов данных:
источник
Чтобы ответить на основную суть вашего вопроса, вы хотите, чтобы атрибут dict только для чтения являлся неизменным источником данных:
Я продемонстрирую, как использовать модуль
namedtuple
fromcollections
для достижения этой цели:возвращается
100
источник
И вывод:
источник
Расширяя идею от kjfletch
Вывод:
источник
Хотя дано много ответов, я не смог найти тот, которым я доволен. Я выяснил свое собственное решение, которое делает
property
работу для динамического случая. Источник для ответа на оригинальный вопрос:источник
Что-то, что работает для меня, это:
Вывод
источник
Недавно я столкнулся с аналогичной проблемой, решение, которое я нашел, использует
__getattr__
и__setattr__
для свойств, которые я хочу, чтобы он обрабатывал, все остальное передается оригиналам.источник
Вот простой пример для создания объекта свойства программно.
источник
Единственный способ динамически присоединить свойство - это создать новый класс и его экземпляр с вашим новым свойством.
источник
Многие из предоставленных ответов требуют так много строк для каждого свойства, то есть / и / или - что я бы назвал уродливой или утомительной реализацией из-за повторяемости, требуемой для нескольких свойств и т. Д. Я предпочитаю сводить детали вниз / упрощать их, пока они больше не может быть упрощено или пока это не принесет особой цели.
Вкратце: в завершенных работах, если я повторяю 2 строки кода, я обычно преобразую его в вспомогательную функцию в одну строку и так далее ... Я упрощаю математические или нечетные аргументы, такие как (start_x, start_y, end_x, end_y), до (x, y, w, h) то есть x, y, x + w, y + h (иногда требуется min / max или если w / h отрицательны и реализации не нравится, я вычту из x / у и пресс с ч / ч и т. д.).
Переопределение внутренних методов получения / установки является нормальным способом, но проблема в том, что вы должны сделать это для каждого класса или создать родительский класс для этой базы ... Это не работает для меня, поскольку я предпочел бы свободно выбирать детей / родителей для наследования, дочерних узлов и т. д.
Я создал решение, которое отвечает на вопрос, не используя тип данных Dict для предоставления данных, так как считаю, что ввод данных является утомительным и т. Д.
Мое решение требует, чтобы вы добавили 2 дополнительные строки над вашим классом, чтобы создать базовый класс для класса, в который вы хотите добавить свойства, затем 1 строку на каждый, и у вас есть возможность добавить обратные вызовы для управления данными, сообщать вам об изменениях данных ограничить данные, которые могут быть установлены на основе значения и / или типа данных, и многое другое.
У вас также есть возможность использовать _object.x, _object.x = значение, _object.GetX (), _object.SetX (значение), и они обрабатываются эквивалентно.
Кроме того, значения являются единственными нестатическими данными, которые назначаются экземпляру класса, но фактическое свойство присваивается классу, то есть вещи, которые вы не хотите повторять, повторять не нужно ... Вы может назначить значение по умолчанию, чтобы метод получения не нуждался в нем каждый раз, хотя есть опция для переопределения значения по умолчанию по умолчанию, и есть другая опция, чтобы метод получения возвращал необработанное сохраненное значение путем переопределения возвратов по умолчанию (примечание: этот метод означает, что необработанное значение присваивается только при назначении значения, в противном случае оно равно None - когда значением является Reset, оно присваивает None и т. д.)
Также есть много вспомогательных функций - первое добавленное свойство добавляет в класс примерно 2 помощника для ссылки на значения экземпляра ... Это повторяемые переменные ResetAccessors (_key, ..) (все можно повторить, используя первые именованные аргументы ) и SetAccessors (_key, _value) с возможностью добавления большего числа в основной класс для повышения эффективности - планируется: способ группировать средства доступа вместе, так что если вы склонны сбрасывать несколько одновременно, каждый раз Вы можете назначить их группе и сбросить группу вместо повторения именованных ключей каждый раз, и многое другое.
Сохраненное значение экземпляра / raw хранится в классе. , класс. ссылается на класс Accessor, который содержит статические переменные / значения / функции для свойства. _класс. это свойство, которое вызывается при доступе через класс экземпляра во время установки / получения и т. д.
Accessor _class .__ указывает на класс, но, поскольку он является внутренним, его необходимо назначить в классе, поэтому я решил использовать __Name = AccessorFunc (...) для его присвоения, по одной строке на свойство со многими необязательными аргументы для использования (используя ключевые переменные, потому что их проще и эффективнее идентифицировать и поддерживать) ...
Как я уже упоминал, я также создаю много функций, некоторые из которых используют информацию о функциях доступа, поэтому их не нужно вызывать (поскольку в данный момент это немного неудобно - сейчас вам нужно использовать _class. .FunctionName (_class_instance) , args) - я нашел способ использовать стек / трассировку для получения ссылки на экземпляр, чтобы получить значение, добавив функции, которые либо запускают этот битовый марафон, либо добавив методы доступа к объекту и используя self (названный this, чтобы указать, что они для экземпляра и чтобы сохранить доступ к себе, ссылку на класс AccessorFunc и другую информацию в определениях функций).
Это не совсем сделано, но это фантастическая нога. Примечание. Если вы не используете __Name = AccessorFunc (...) для создания свойств, у вас не будет доступа к клавише __, даже если я определю ее в функции init. Если вы делаете, то нет никаких проблем.
Также: Обратите внимание, что Имя и Ключ разные ... Имя является «формальным», используется при создании имени функции, а ключ предназначен для хранения и доступа к данным. то есть _class.x, где строчная буква x - это ключ, имя будет прописной буквой X, так что GetX () - это функция вместо Getx (), которая выглядит немного странно. это позволяет self.x работать и выглядеть соответствующим образом, но также позволяет GetX () и выглядеть соответствующим образом.
У меня есть пример класса с ключом / именем, идентичным, и другим, чтобы показать. Для вывода данных создано множество вспомогательных функций (Примечание: не все это завершено), чтобы вы могли видеть, что происходит.
Текущий список функций, использующих key: x, name: X, выводится как:
Это ни в коем случае не исчерпывающий список - есть некоторые, которые не вошли в этот список на момент публикации ...
Некоторые из выводимых данных:
Это для совершенно нового класса, созданного с использованием демо-класса без каких-либо данных, назначенных, кроме имени (чтобы его можно было выводить), то есть _foo, имя переменной, которую я использовал ...
И это после присвоения всем свойствам _foo (кроме имени) следующих значений в том же порядке: 'string', 1.0, True, 9, 10, False
Обратите внимание, что из-за ограниченных типов данных или ограничений по значению некоторые данные не были назначены - это сделано специально. Сеттер запрещает присваивать неверные типы данных или значения, даже если они назначаются в качестве значения по умолчанию (если вы не переопределите поведение защиты значений по умолчанию)
Код не был размещен здесь, потому что у меня не было места после примеров и объяснений ... Кроме того, потому что он изменится.
Пожалуйста, обратите внимание: во время этой публикации, файл грязный - это изменится. Но если вы запустите его в Sublime Text и скомпилируете его или запустите из Python, он скомпилирует и выплюнет кучу информации - часть AccessorDB не сделана (которая будет использоваться для обновления Print Getters и помощника GetKeyOutput функции вместе с изменением на функцию Instance, возможно, помещенную в одну функцию и переименованную - ищите ее ..)
Далее: не все требуется для его запуска - большая часть комментариев внизу предназначена для дополнительной информации, используемой для отладки - ее может не быть при загрузке. Если это так, вы сможете раскомментировать и перекомпилировать, чтобы получить больше информации.
Я ищу обходной путь к необходимости MyClassBase: pass, MyClass (MyClassBase): ... - если вы знаете о решении - опубликуйте его.
Единственное, что необходимо в классе - это __-строки - str для отладки, как и init - их можно удалить из демонстрационного класса, но вам нужно будет закомментировать или удалить некоторые из строк ниже (_foo / 2/3 ) ..
Классы String, Dict и Util вверху являются частью моей библиотеки Python - они не завершены. Я скопировал из библиотеки несколько вещей, которые мне были нужны, и создал несколько новых. Полный код будет связан с полной библиотекой и будет включать его вместе с предоставлением обновленных вызовов и удалением кода (фактически, единственным оставшимся кодом будет демонстрационный класс и операторы печати - система AccessorFunc будет перемещена в библиотеку). ..
Часть файла:
Эта красота делает невероятно простым создание новых классов с динамически добавленными свойствами с помощью AccessorFuncs / callbacks / data-type / value принудительного применения и т. Д.
На данный момент ссылка на (Эта ссылка должна отражать изменения в документе.): Https://www.dropbox.com/s/6gzi44i7dh58v61/dynamic_properties_accessorfuncs_and_more.py?dl=0.
Кроме того: если вы не используете Sublime Text, я рекомендую его для Notepad ++, Atom, Visual Code и других из-за правильной реализации многопоточности, которая делает его намного, намного быстрее в использовании ... Я также работаю над IDE-подобным кодом Система сопоставления для него - взгляните на: https://bitbucket.org/Acecool/acecoolcodemappingsystem/src/master/ (сначала добавьте репо в диспетчере пакетов, затем установите плагин - когда версия 1.0.0 будет готова, я добавлю это к основному списку плагинов ...)
Я надеюсь, что это решение поможет ... и, как всегда:
Просто потому, что это работает, не делает это правильно - Джош 'Acecool' Мозер
источник