区分从 class(或文件)内部和从外部 python 访问 class 属性
Differentiate accessing class attribute from within the class (or file) and from outside in python
让我们举个例子 class Foo in Python:
class Foo:
bar = 'bar'
def access_bar(self):
return self.bar
我可以,例如,在直接访问 Foo().bar
时打印警告,但同时在调用 Foo().access_bar()
时不打印此警告,它从 [=27] 中访问该属性=]?
我尝试实施 __getattribute__
方法,但无法区分这些情况。
我知道这是一个很奇怪的问题,但请不要像 'You should not need this' 那样回答我。
您可以将 bar
设为 属性,这样可以在不向外部显示方法调用的情况下控制访问,并将您的属性设为私有:
class Foo:
__bar = 'bar'
@property
def bar(self):
print("direct access")
return Foo.__bar
def access_bar(self):
return self.__bar
f = Foo()
print("warn",f.bar)
print("OK",f.access_bar())
打印:
direct access
warn bar
OK bar
我建议将值存储在受保护(一个前导下划线)或私有(两个下划线)属性中,并使 bar
成为可以安全访问的 属性,相当于 access_bar
在你的问题中。在 Python.
中,通常就是这样做的
class Foo:
_bar = 'bar'
@property
def bar(self):
# do extra things here
return self._bar
用户仍然可以编写 foo._bar
或 foo._Foo__bar
(对于私有属性)以在没有任何警告的情况下从外部获取属性,但是如果他们知道围绕前导下划线的约定,他们可能会这样做有点不舒服,请注意风险。
这是您问题的 'real' 答案,您可能不应该这样做:
import inspect
class Foo:
bar = 'bar'
def access_bar(self):
return self.bar
def __getattribute__(self, item):
if item == 'bar':
code = inspect.currentframe().f_back.f_code
if not (start_lineno <= code.co_firstlineno <= end_lineno
and code.co_filename == __file__):
print('Warning: accessing bar directly')
return super().__getattribute__(item)
lines, start_lineno = inspect.getsourcelines(Foo)
end_lineno = start_lineno + len(lines) - 1
print(1, Foo().bar)
print(2, Foo().access_bar())
如果您这样做,重要的是文件中只有一个名为 Foo
的 class,否则 inspect.getsourcelines(Foo)
可能不会给出正确的结果。
这是另一个改进 的尝试,方法是添加元 class 以便它也适用于 class 属性,并取消 inspect
模块,取而代之的是向 __getattribute__
函数本身添加一个警告标志。
class FooType(type):
def __getattribute__(self, item):
if item == "bar":
print("Warning: accessing bar directly from class")
return item.__getattribute__(self, item)
class Foo(object, metaclass=FooType):
bar = 'bar'
def access_bar(self):
return self.__getattribute__('bar', warn=False)
def __getattribute__(self, item, warn=True):
if item == 'bar' and warn:
print('Warning: accessing bar directly from instance')
return super().__getattribute__(item)
print(Foo.bar)
#Warning: accessing bar directly from class
#bar
print(Foo().bar)
#Warning: accessing bar directly from instance
#bar
print(Foo().access_bar())
#bar
让我们举个例子 class Foo in Python:
class Foo:
bar = 'bar'
def access_bar(self):
return self.bar
我可以,例如,在直接访问 Foo().bar
时打印警告,但同时在调用 Foo().access_bar()
时不打印此警告,它从 [=27] 中访问该属性=]?
我尝试实施 __getattribute__
方法,但无法区分这些情况。
我知道这是一个很奇怪的问题,但请不要像 'You should not need this' 那样回答我。
您可以将 bar
设为 属性,这样可以在不向外部显示方法调用的情况下控制访问,并将您的属性设为私有:
class Foo:
__bar = 'bar'
@property
def bar(self):
print("direct access")
return Foo.__bar
def access_bar(self):
return self.__bar
f = Foo()
print("warn",f.bar)
print("OK",f.access_bar())
打印:
direct access
warn bar
OK bar
我建议将值存储在受保护(一个前导下划线)或私有(两个下划线)属性中,并使 bar
成为可以安全访问的 属性,相当于 access_bar
在你的问题中。在 Python.
class Foo:
_bar = 'bar'
@property
def bar(self):
# do extra things here
return self._bar
用户仍然可以编写 foo._bar
或 foo._Foo__bar
(对于私有属性)以在没有任何警告的情况下从外部获取属性,但是如果他们知道围绕前导下划线的约定,他们可能会这样做有点不舒服,请注意风险。
这是您问题的 'real' 答案,您可能不应该这样做:
import inspect
class Foo:
bar = 'bar'
def access_bar(self):
return self.bar
def __getattribute__(self, item):
if item == 'bar':
code = inspect.currentframe().f_back.f_code
if not (start_lineno <= code.co_firstlineno <= end_lineno
and code.co_filename == __file__):
print('Warning: accessing bar directly')
return super().__getattribute__(item)
lines, start_lineno = inspect.getsourcelines(Foo)
end_lineno = start_lineno + len(lines) - 1
print(1, Foo().bar)
print(2, Foo().access_bar())
如果您这样做,重要的是文件中只有一个名为 Foo
的 class,否则 inspect.getsourcelines(Foo)
可能不会给出正确的结果。
这是另一个改进 inspect
模块,取而代之的是向 __getattribute__
函数本身添加一个警告标志。
class FooType(type):
def __getattribute__(self, item):
if item == "bar":
print("Warning: accessing bar directly from class")
return item.__getattribute__(self, item)
class Foo(object, metaclass=FooType):
bar = 'bar'
def access_bar(self):
return self.__getattribute__('bar', warn=False)
def __getattribute__(self, item, warn=True):
if item == 'bar' and warn:
print('Warning: accessing bar directly from instance')
return super().__getattribute__(item)
print(Foo.bar)
#Warning: accessing bar directly from class
#bar
print(Foo().bar)
#Warning: accessing bar directly from instance
#bar
print(Foo().access_bar())
#bar