Если у вас есть базовый класс A
и производный класс B
, вы можете сделать следующее.
void wantAnA(A myA)
{
// work with myA
}
B derived;
// work with the object "derived"
wantAnA(derived);
Теперь для метода wantAnA
нужна копия derived
, Однако объект derived
не может быть полностью скопирован, так как класс B
может изобретать дополнительные переменные-члены, которые не находятся в его базовом классе A
.
Поэтому, чтобы вызвать wantAnA
, компилятор будет «срезать» все дополнительные члены производного класса. Результатом может быть объект, который вы не хотите создавать, потому что
A
(все особые поведения класс B
теряется). В глобальном пространстве имен это невозможно, но вы можете воспользоваться более продвинутым метапрограммированием Python, чтобы предотвратить создание нескольких экземпляров объекта Protect
. Singleton pattern является хорошим примером этого.
В случае Singleton вы должны убедиться, что после создания экземпляра, даже если исходная переменная, ссылающаяся на экземпляр, переназначена, объект будет сохраняться. Любые последующие экземпляры просто вернут ссылку на тот же объект.
Несмотря на этот шаблон, вы никогда не сможете предотвратить переназначение имени глобальной переменной.
Я не думаю, что это возможно. То, как я его вижу, присваивание переменной ничего не делает с объектом, о котором она ранее говорила: это просто, что переменная «указывает» на другой объект.
In [3]: class My():
...: def __init__(self, id):
...: self.id=id
...:
In [4]: a = My(1)
In [5]: b = a
In [6]: a = 1
In [7]: b
Out[7]: <__main__.My instance at 0xb689d14c>
In [8]: b.id
Out[8]: 1 # the object is unchanged!
Однако вы может имитировать желаемое поведение, создавая объект-оболочку с помощью методов __setitem__()
или __setattr__()
, которые создают исключение, и сохраняют «неизменяемый» материал внутри.
Нет, поскольку присвоение является внутренним языком , который не имеет крючка модификации.
Уродливым решением является переназначение деструктора. Но это не реальное назначение перегрузки.
import copy
global a
class MyClass():
def __init__(self):
a = 1000
# ...
def __del__(self):
a = copy.copy(self)
a = MyClass()
a = 1
Используя пространство имен верхнего уровня, это невозможно. Когда вы запускаете
`var = 1`
, он сохраняет ключ var
и значение 1
в глобальном словаре. Это примерно эквивалентно вызову globals().__setitem__('var', 1)
. Проблема в том, что вы не можете заменить глобальный словарь в запущенном скрипте (вы, вероятно, можете, возившись со стеком, но это не очень хорошая идея). Однако вы можете выполнять код во вторичном пространстве имен и предоставлять собственный словарь для своих глобальных переменных.
class myglobals(dict):
def __setitem__(self, key, value):
if key=='val':
raise TypeError()
dict.__setitem__(self, key, value)
myg = myglobals()
dict.__setitem__(myg, 'val', 'protected')
import code
code.InteractiveConsole(locals=myg).interact()
Это запустит REPL, который почти работает нормально, но отказывается от любых попыток установить переменную val
. Вы также можете использовать execfile(filename, myg)
. Обратите внимание, что это не защищает от вредоносного кода.
Да, возможно, вы можете обрабатывать __assign__
с помощью модификации ast
.
pip install assign
class T():
def __assign__(self, v):
print('called with %s' % v)
b = T()
c = b
>>> import magic
>>> import test
called with c
Проект находится в https://github.com/RyanKung/assign
. Более простой смысл: https://gist.github.com/RyanKung/4830d6c8474e6bcefa4edd13f122b4df
print('called with %s' % self)
?
– zezollo
9 November 2017 в 17:34
То, как вы это описываете, абсолютно невозможно. Назначение имени является фундаментальной особенностью Python, и для изменения его поведения не предусмотрены перехватчики.
Однако назначение члену экземпляра класса можно контролировать , поскольку вы хотите, переопределив .__setattr__()
.
class MyClass(object):
def __init__(self, x):
self.x = x
self._locked = True
def __setattr__(self, name, value):
if self.__dict__.get("_locked", False) and name == "x":
raise AttributeError("MyClass does not allow assignment to .x member")
self.__dict__[name] = value
>>> m = MyClass(3)
>>> m.x
3
>>> m.x = 4
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 7, in __setattr__
AttributeError: MyClass does not allow assignment to .x member
Обратите внимание, что существует переменная-член, _locked
, которая контролирует, разрешено ли присвоение. Вы можете разблокировать его, чтобы обновить значение.
@property
с геттером, но нет сеттера, аналогично назначению псевдоперегрузки.
– jtpereyda
16 December 2015 в 21:26
getattr(self, "_locked", None)
вместо self.__dict__.get("_locked")
.
– Vedran Šego
9 April 2018 в 13:50
False
вместо None
. Теперь, если кто-то удалит переменную-член _locked
, вызов .get()
не вызовет исключения.
– steveha
30 May 2018 в 06:09
get
по умолчанию None
, в отличие от getattr
, который действительно вызвал бы исключение.
– Vedran Šego
30 May 2018 в 09:08
getattr()
, а не .__dict__.get()
. Думаю, лучше использовать getattr()
, вот для чего это.
– steveha
7 June 2018 в 04:44
Нет, нет
Подумайте об этом, в вашем примере вы пересобираете имя var на новое значение. Вы на самом деле не касаетесь экземпляра Protect.
Если имя, которое вы хотите переустановить, на самом деле является свойством какого-либо другого объекта, то есть myobj.var, тогда вы можете предотвратить присвоение значения атрибуту property / attribute сущности. Но я предполагаю, что это не то, что вы хотите от вашего примера.
__dict__.__setattr__
, но module.__dict__
сам доступен только для чтения. Кроме того, введите (mymodule) == & lt; type 'module' & gt ;, и он не является экземпляром.
– Caruccio
14 June 2012 в 12:53
Как правило, лучший подход, который я нашел, переопределяет __ilshift__
в качестве сеттера и __rlshift__
в качестве геттера, дублируется декоратором свойств. Это почти последний оператор, разрешаемый только (| & amp; ^), а логический - ниже. Он редко используется (__lrshift__
меньше, но его можно принимать во внимание).
В рамках использования пакета назначения PyPi можно управлять только прямым назначением, поэтому фактическая «сила» оператора ниже , PyPi присваивает пример пакета:
class Test:
def __init__(self, val, name):
self._val = val
self._name = name
self.named = False
def __assign__(self, other):
if hasattr(other, 'val'):
other = other.val
self.set(other)
return self
def __rassign__(self, other):
return self.get()
def set(self, val):
self._val = val
def get(self):
if self.named:
return self._name
return self._val
@property
def val(self):
return self._val
x = Test(1, 'x')
y = Test(2, 'y')
print('x.val =', x.val)
print('y.val =', y.val)
x = y
print('x.val =', x.val)
z: int = None
z = x
print('z =', z)
x = 3
y = x
print('y.val =', y.val)
y.val = 4
output:
x.val = 1
y.val = 2
x.val = 2
z = <__main__.Test object at 0x0000029209DFD978>
Traceback (most recent call last):
File "E:\packages\pyksp\pyksp\compiler2\simple_test2.py", line 44, in <module>
print('y.val =', y.val)
AttributeError: 'int' object has no attribute 'val'
То же самое со сдвигом:
class Test:
def __init__(self, val, name):
self._val = val
self._name = name
self.named = False
def __ilshift__(self, other):
if hasattr(other, 'val'):
other = other.val
self.set(other)
return self
def __rlshift__(self, other):
return self.get()
def set(self, val):
self._val = val
def get(self):
if self.named:
return self._name
return self._val
@property
def val(self):
return self._val
x = Test(1, 'x')
y = Test(2, 'y')
print('x.val =', x.val)
print('y.val =', y.val)
x <<= y
print('x.val =', x.val)
z: int = None
z <<= x
print('z =', z)
x <<= 3
y <<= x
print('y.val =', y.val)
y.val = 4
output:
x.val = 1
y.val = 2
x.val = 2
z = 2
y.val = 3
Traceback (most recent call last):
File "E:\packages\pyksp\pyksp\compiler2\simple_test.py", line 45, in <module>
y.val = 4
AttributeError: can't set attribute
Таким образом, оператор <<=
, получающий значение в свойстве, является гораздо более визуально чистым решением и не пытается заставить пользователя совершить некоторые отражающие ошибки, такие как:
var1.val = 1
var2.val = 2
# if we have to check type of input
var1.val = var2
# but it could be accendently typed worse,
# skipping the type-check:
var1.val = var2.val
# or much more worse:
somevar = var1 + var2
var1 += var2
# sic!
var1 = var2
var = 1
не вызывает одноэлементный механизм. – Caruccio 14 June 2012 в 12:54Protect()
). Невозможно защитить первоначально назначенное имя (например,var
). – jathanism 14 June 2012 в 19:11