在python中从类(或文件)内部和外部区分访问类属性

时间:2019-02-20 09:01:55

标签: python python-3.7

让我们在Python中有一个示例类Foo:

class Foo:
    bar = 'bar'
    def access_bar(self):
        return self.bar  

例如,当我直接访问Foo().bar时,是否可以打印警告,但是同时调用Foo().access_bar()时(从类内部访问该属性)时不能打印此警告?

我尝试实现__getattribute__方法,但是区分这些情况没有运气。

我知道这是一个很奇怪的问题,但是请不要像“您不需要此”那样回答我。

4 个答案:

答案 0 :(得分:2)

您可以将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

答案 1 :(得分:2)

这是您问题的“真实”答案,您可能不应该这样做:

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的类很重要,否则inspect.getsourcelines(Foo)可能无法给出正确的结果。

答案 2 :(得分:1)

我建议将值存储在受保护的(一个下划线)或私有的(两个下划线)属性中,并将bar设置为可以安全访问的属性,相当于您问题中的access_bar 。这就是通常在Python中完成这种事情的方式。

class Foo:
    _bar = 'bar'

    @property
    def bar(self):
        # do extra things here
        return self._bar

用户仍然可以编写foo._barfoo._Foo__bar(用于私有属性),以在没有任何警告的情况下从外部获取该属性,但是,如果他们知道围绕下划线的约定,他们可能会有所感触这样做会很不舒服,并要注意风险。

答案 3 :(得分:1)

这是通过添加元类以使其也适用于类属性并取消使用inspect模块,而是向__getattribute__添加警告标志来改进Alex's answer的另一种尝试。功能本身。

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