【问题标题】:Python: Possible to make attribute access strictly private?Python:可以使属性访问严格私有吗?
【发布时间】:2018-04-24 21:28:40
【问题描述】:

如果我有一个包含字典的类,我需要在各种实例方法中重用该字典,但我想让该字典只能从特定实例方法写入,这可能吗?

在这段代码中:

class GoodClass:
    def __init__(self):
        # name mangling enabled!
        self.__dict = {}

    def __repr__(self):
        return str(self.__dict)

    def add_to_dict(self, key, item):
        # this should be the only method that can write to self.__dict
        try:
            self.__dict[key] += [item]
        except KeyError:
            self.__dict[key] = [item]

    def make_items(self, n_items=3, important_param=1):
        # do the important stuff
        for i in range(n_items):
            item = "param: {}".format(important_param)

            self.add_to_dict("important_items", item)

    def this_is_bad(self):
        # don't want this to be possible
        self.__dict["this is bad"] = ["quite bad"]

c = GoodClass()

c.make_items()
c.this_is_bad()

# c.__dict["can't do this"] = ["thanks mangling!"]

print(c)
# {'important_items': ['param: 1', 'param: 1', 'param: 1'], 'this is bad': ['quite bad']}

有没有办法确保add_to_dict 是唯一可以写入字典的方法,就像修改防止从类外部写入字典一样?

我的用例在 IronPython 的托管版本中,因此inspect.currentframe 不能像下面几个答案中提到的那样工作。不过,这应该适用于非托管 IronPython 或其他版本。

【问题讨论】:

  • ...为什么?即使在带有private 说明符的语言中,我也想不出有这样的功能。
  • 特别是,如果您希望其他方法能够读取 self.__dict,并且您希望__init__ 能够初始化 @ 987654327@,但您只希望一种特定的方法能够将项目添加到 self.__dict,这不是 Python 的对象模型的工作方式。
  • 假设您可以使用__setattr__ 魔术方法来做到这一点,但@user2357112 是正确的,我的回答是......为什么?
  • 公开阅读不是问题,可以有一个GoodClass.dict 属性。这个想法是防止另一种方法直接覆盖self.__dict["important_items"]
  • 您可以将self.__dict 重命名为self._only_write_from_add_to_dict_method(既易于实现,又对类用户而言显而易见)。

标签: python ironpython


【解决方案1】:

以下是如何使 dict 只允许从正确的位置设置值:

import inspect

class GoodDict(dict):
    __allowed_codes = {GoodClass.add_to_dict.__code__}

    def __setitem__(self, key, value):
        frame = inspect.currentframe()
        while frame:
            if frame.f_code in GoodDict.__allowed_codes:
                break
            frame = frame.f_back
        else:
            raise Exception('__setitem__ called from non-allowed function')

        super().__setitem__(key, value)

如果从字典中删除是一个问题,你还应该实现__delitem__

然后在GoodClass:self.__dict = GoodDict()中使用。

最后,您应该将值设为元组而不是列表,以确保不变性。

【讨论】:

  • 是的,我在想我可能必须继承 dict 才能正确地做到这一点。不幸的是我不能使用inspect.currentframe
  • @tykom 我刚刚意识到这可以通过编写dict.__setitem__(self.__dict, "this is bad", ["quite bad"]) 轻松绕过。您希望覆盖的难度有多大?
  • 理想情况下,与名称修改一样难,可以从课堂外写入。但也许这有点矫枉过正......丢失 dict 中的信息可能最终会污染数千张正在创建的图像
【解决方案2】:

虽然我不知道你为什么要这样做,但我认为这对你有用:

第一种方法:

class my_class:
    def __init__(self):
        pass
    @property
    def some_variable(self):
        return self.__some_variable

    @some_variable.setter
    def some_variable(self, value):
        current_frame = inspect.currentframe()
        calling_function = inspect.getouterframes(current_frame, 2)[1][3]
        if (calling_function != "allowed_func_name"):
            raise Exception()
        self.__some_variable = value

第二种方法:

class my_class:
    def __init__(self):
        self.some_variable_set_flag = False
        pass
    def some_func(self, value):
        self.some_variable_set_flag = True
        try :
            self.some_variable = value
        finally :
            self.some_variable_set_flag = False

    @property
    def some_variable(self):
        return self.__some_variable

    @some_variable.setter
    def some_variable(self, value):
        if (not self.some_variable_set_flag):
            raise Exception()
        self.__some_variable = value
  • 这种方法不会为您提供完整的保护,但需要手动覆盖。

它以一种抽象的方式(如果其他人有时间会寻找这个)。

【讨论】:

  • 不过,这可能对其他人有用。不幸的是,我的用例是 IronPython 的托管版本,currentframe 返回None。不过这还不清楚,所以我会更新我的问题。谢谢!
  • 您可以尝试类似的方法,它不会完全阻止它,但会给您某种保护,编辑不当。 @tykom 已编辑。
  • 要格外小心,请使用try/finally 以确保self.some_variable_set_flag = False 在最后运行。
  • 是的,我认为标记方法和try/finally 是最干净的。我希望有一种对象级的方法来做到这一点。感谢您的帮助!
  • @tykom 睡在上面,可能会想到更好的方法。主要问题是不知道调用者的身份(由于检查无返回)。
猜你喜欢
  • 2014-12-10
  • 1970-01-01
  • 1970-01-01
  • 2014-08-17
  • 1970-01-01
  • 2013-06-22
  • 1970-01-01
  • 2012-01-03
  • 2020-05-07
相关资源
最近更新 更多