Как добавить свойство к классу динамично?

Похоже, вы пытаетесь передать свой MyWebViewClient класс в качестве экземпляра в этой строке:

myWebView.setWebViewClient(MyWebViewClient);

может попробовать что-то вроде этого:

myWebView.setWebViewClient(new MyWebViewClient());

или [116 ]

MyWebViewClient myWebviewClientInstance = new MyWebViewClient();
myWebView.setWebViewClient(myWebviewClientInstance);
192
задан Boann 3 March 2017 в 13:55
поделиться

11 ответов

Полагаю, мне следует расширить этот ответ, теперь, когда я стал старше и мудрее и знаю, что происходит. Лучше поздно, чем никогда.

Вы можете добавить свойство к классу динамически. Но в этом загвоздка: вы должны добавить его в класс .

>>> class Foo(object):
...     pass
... 
>>> foo = Foo()
>>> foo.a = 3
>>> Foo.b = property(lambda self: self.a + 1)
>>> foo.b
4

Свойство на самом деле является простой реализацией того, что называется дескриптором . Это объект, который обеспечивает настраиваемую обработку для данного атрибута в данном классе . Вроде как способ выделить огромное if дерево из __ getattribute __ .

Когда я прошу foo.b в примере выше, Python видит, что b , определенный в классе, реализует протокол дескриптора - что просто означает, что это объект с __ get __ , __ set __ или __ delete __ метод. Дескриптор берет на себя ответственность за обработку этого атрибута, поэтому Python вызывает Foo.b .__ get __ (foo, Foo) , и возвращаемое значение возвращается вам как значение атрибута. В случае свойства каждый из этих методов просто вызывает fget , fset или fdel , которые вы передали в property конструктор.

Дескрипторы - это на самом деле способ Python раскрыть всю внутреннюю структуру своей объектно-ориентированной реализации. Фактически, есть другой тип дескриптора, даже более распространенный, чем свойство .

>>> class Foo(object):
...     def bar(self):
...         pass
... 
>>> Foo().bar
<bound method Foo.bar of <__main__.Foo object at 0x7f2a439d5dd0>>
>>> Foo().bar.__get__
<method-wrapper '__get__' of instancemethod object at 0x7f2a43a8a5a0>

Скромный метод - это просто еще один вид дескриптора. Его __ get __ указывает на вызывающий экземпляр в качестве первого аргумента; по сути, он делает это:

def __get__(self, instance, owner):
    return functools.partial(self.function, instance)

В любом случае, Я подозреваю, что именно поэтому дескрипторы работают только с классами: они формализация того, что в первую очередь поддерживает классы. Они даже являются исключением из правил: вы, очевидно, можете назначать дескрипторы классу, а классы сами являются экземплярами типа ! Фактически, попытка прочитать Foo.b по-прежнему вызывает свойство .__ get __ ; это просто идиоматично, когда дескрипторы возвращаются при обращении к ним как атрибуты класса.

Я думаю, это здорово, что практически вся объектно-ориентированная система Python может быть выражена на Python. :)

О, и я написал многословный пост в блоге о дескрипторах некоторое время назад, если вам интересно.

Это даже исключение из правила: вы, очевидно, можете назначать дескрипторы классу, а классы сами являются экземплярами типа ! Фактически, попытка прочитать Foo.b по-прежнему вызывает свойство .__ get __ ; это просто идиоматично, когда дескрипторы возвращаются при обращении к ним как атрибуты класса.

Я думаю, это здорово, что практически вся объектно-ориентированная система Python может быть выражена на Python. :)

О, и я написал многословный пост в блоге о дескрипторах некоторое время назад, если вам интересно.

Это даже исключение из правила: вы, очевидно, можете назначать дескрипторы классу, а классы сами являются экземплярами типа ! Фактически, попытка прочитать Foo.b по-прежнему вызывает свойство .__ get __ ; это просто идиоматично, когда дескрипторы возвращаются при обращении к ним как атрибуты класса.

Я думаю, это довольно круто, что практически вся объектно-ориентированная система Python может быть выражена на Python. :)

О, и я написал многословный пост в блоге о дескрипторах некоторое время назад, если вам интересно.

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

Я думаю, это довольно круто, что практически вся объектно-ориентированная система Python может быть выражена на Python. :)

О, и я написал многословный пост в блоге о дескрипторах некоторое время назад, если вам интересно.

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

Я думаю, это довольно круто, что практически вся объектно-ориентированная система Python может быть выражена на Python. :)

О, и я написал многословный пост в блоге о дескрипторах некоторое время назад, если вам интересно.

308
ответ дан 23 November 2019 в 05:29
поделиться

Вот решение что:

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

После того, как класс был определен, Вы просто делаете это для добавления свойства к нему динамично:

setattr(SomeClass, 'propertyName', property(getter, setter))

Вот полный пример, протестированный в Python 3:

#!/usr/bin/env python3

class Foo():
  pass

def get_x(self):
  return 3

def set_x(self, value):
  print("set x on %s to %d" % (self, value))

setattr(Foo, 'x', property(get_x, set_x))

foo1 = Foo()
foo1.x = 12
print(foo1.x)
1
ответ дан 23 November 2019 в 05:29
поделиться

Единственный способ динамически присоединить свойство - создать новый класс и его экземпляр с вашим новым свойством.

class Holder: p = property(lambda x: vs[i], self.fn_readonly)
setattr(self, k, Holder().p)
-1
ответ дан 23 November 2019 в 05:29
поделиться

Вы не можете добавить новое свойство () в экземпляр во время выполнения, потому что свойства являются дескрипторами данных. Вместо этого вы должны динамически создать новый класс или перегрузить __ getattribute __ , чтобы обрабатывать дескрипторы данных в экземплярах.

5
ответ дан 23 November 2019 в 05:29
поделиться

Не уверен, что я полностью понимаю вопрос, но вы можете изменить свойства экземпляра во время выполнения с помощью встроенного __ dict __ вашего class:

class C(object):
    def __init__(self, ks, vs):
        self.__dict__ = dict(zip(ks, vs))


if __name__ == "__main__":
    ks = ['ab', 'cd']
    vs = [12, 34]
    c = C(ks, vs)
    print(c.ab) # 12
3
ответ дан 23 November 2019 в 05:29
поделиться

Похоже, вы могли бы решить эту проблему гораздо проще с помощью namedtuple , поскольку вы заранее знаете весь список полей.

from collections import namedtuple

Foo = namedtuple('Foo', ['bar', 'quux'])

foo = Foo(bar=13, quux=74)
print foo.bar, foo.quux

foo2 = Foo()  # error

Если вам абсолютно необходимо напишите свой собственный сеттер, вам придется выполнять метапрограммирование на уровне класса; property () не работает с экземплярами.

34
ответ дан 23 November 2019 в 05:29
поделиться

Для этого не нужно использовать свойство. Просто переопределите __ setattr __ , чтобы они были доступны только для чтения.

class C(object):
    def __init__(self, keys, values):
        for (key, value) in zip(keys, values):
            self.__dict__[key] = value

    def __setattr__(self, name, value):
        raise Exception("It is read only!")

Тада.

>>> c = C('abc', [1,2,3])
>>> c.a
1
>>> c.b
2
>>> c.c
3
>>> c.d
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'C' object has no attribute 'd'
>>> c.d = 42
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 6, in __setattr__
Exception: It is read only!
>>> c.a = 'blah'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 6, in __setattr__
Exception: It is read only!
31
ответ дан 23 November 2019 в 05:29
поделиться

Лучший способ добиться этого - определить __ slots __ . Таким образом, ваши экземпляры не могут иметь новые атрибуты.

ks = ['ab', 'cd']
vs = [12, 34]

class C(dict):
    __slots__ = []
    def __init__(self, ks, vs): self.update(zip(ks, vs))
    def __getattr__(self, key): return self[key]

if __name__ == "__main__":
    c = C(ks, vs)
    print c.ab

Это печатает 12

    c.ab = 33

Это дает: AttributeError: объект 'C' не имеет атрибута 'ab'

2
ответ дан 23 November 2019 в 05:29
поделиться

Я задал аналогичный вопрос в этом сообщении о переполнении стека , чтобы создать фабрику классов, которая создает простые типы. Результатом был этот ответ , в котором была рабочая версия фабрики классов. Вот отрывок ответа:

def Struct(*args, **kwargs):
    def init(self, *iargs, **ikwargs):
        for k,v in kwargs.items():
            setattr(self, k, v)
        for i in range(len(iargs)):
            setattr(self, args[i], iargs[i])
        for k,v in ikwargs.items():
            setattr(self, k, v)

    name = kwargs.pop("name", "MyStruct")
    kwargs.update(dict((k, None) for k in args))
    return type(name, (object,), {'__init__': init, '__slots__': kwargs.keys()})

>>> Person = Struct('fname', 'age')
>>> person1 = Person('Kevin', 25)
>>> person2 = Person(age=42, fname='Terry')
>>> person1.age += 10
>>> person2.age -= 10
>>> person1.fname, person1.age, person2.fname, person2.age
('Kevin', 35, 'Terry', 32)
>>>

Вы можете использовать некоторые варианты этого для создания значений по умолчанию, что и является вашей целью (в этом вопросе также есть ответ).

5
ответ дан 23 November 2019 в 05:29
поделиться

Цель состоит в том, чтобы создать фиктивный класс, который ведет себя как набор результатов db.

Так что вам нужен словарь, в котором вы можете написать ['b'] как ab?

Это просто:

class atdict(dict):
    __getattr__= dict.__getitem__
    __setattr__= dict.__setitem__
    __delattr__= dict.__delitem__
56
ответ дан 23 November 2019 в 05:29
поделиться

Кажется, это работает (но см. Ниже):

class data(dict,object):
    def __init__(self,*args,**argd):
        dict.__init__(self,*args,**argd)
        self.__dict__.update(self)
    def __setattr__(self,name,value):
        raise AttributeError,"Attribute '%s' of '%s' object cannot be set"%(name,self.__class__.__name__)
    def __delattr__(self,name):
        raise AttributeError,"Attribute '%s' of '%s' object cannot be deleted"%(name,self.__class__.__name__)

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

edit

Вероятно, следующего будет больше. эффективное использование памяти для больших наборов данных:

class data(dict,object):
    def __init__(self,*args,**argd):
        dict.__init__(self,*args,**argd)
    def __getattr__(self,name):
        return self[name]
    def __setattr__(self,name,value):
        raise AttributeError,"Attribute '%s' of '%s' object cannot be set"%(name,self.__class__.__name__)
    def __delattr__(self,name):
        raise AttributeError,"Attribute '%s' of '%s' object cannot be deleted"%(name,self.__class__.__name__)
0
ответ дан 23 November 2019 в 05:29
поделиться
Другие вопросы по тегам:

Похожие вопросы: