python对象的“权限”控制
本文将对权限控制这个问题进行简单探讨
阅读前说明
此篇并不是一篇技术教程,而是本人对python对象的权限的一点不一样的理解
以下内容仅为个人理解,如有偏差之处请自行忽略
或许是受到C#的设计哲学的影响,又或许是对代码质量的要求,在做一些项目开发时对各种对象、变量的权限控制有了更高的要求,有些变量涉及到外部交互,需要设置为public; 而有些变量不希望被外部访问,需要设置为private等。
可能有的人会想,有必要这么在意这些问题吗?诚然,如果你只是个人开发,似乎全部使用public属性也没有什么不妥,也不会影响开发1,就目前我个人的开发经验来看,可能涉及到多人协作开发的时候,做好必要的限制是有必要的,对于这些细节相信读者都在python面向对象部分的教程中都有听过,这里就不作过多的细节介绍。
在某次阅读某本书籍时,意外发现python中也有类似protected属性的概念,也就是使用单个下划线,即_variable的写法,但随着开发,发现python的protected属性与其他OOP语言(比如C#)还不太一样,以下举一些例子来说明
首先先简单说明protected属性的意义是什么。以上我提到变量可以设置为public和private,但如果涉及到继承的时候,使用public会导致外部被访问,但使用private又无法被继承,可是我既想要让继承类获取父类的对象,但又不希望外部被访问,这个时候protected属性就起到作用了。
看起来很美好,但是。我要说但是,python的protected属性和C#提供的有些不一样2。以下是两段代码示例
说明
以下代码看不懂不需要担心,直接看注释
class Student:
def __init__(self, name, age):
self.__name = name # 定义private属性的name
self.age = age # 定义public属性的age
self._id = 1 # 定义protected属性的id
student = Student("gcc", "10")
# 尝试访问name
try:
print(student.name)
except Exception as e:
print(e)
# 'Student' object has no attribute 'name'
# 尝试访问age
try:
print(student.age)
except Exception as e:
print(e)
# 10
# 尝试访问id
try:
print(student._id)
except Exception as e:
print(e)
# 1
# 尝试修改id
try:
student._id += 1
print(student._id)
except Exception as e:
print(e)
# 2
var student = new Student("gcc", 10);
// 尝试访问name
try
{
Console.WriteLine(student.Name);
}
catch (Exception e)
{
Console.WriteLine(e);
}
// 无法编译通过
// 尝试访问age
try
{
Console.WriteLine(student.Age);
}
catch (Exception e)
{
Console.WriteLine(e);
}
// 10
// 尝试访问id
try
{
Console.WriteLine(student.Id);
}
catch (Exception e)
{
Console.WriteLine(e);
}
// 无法编译通过
class Student
{
private string Name; // 定义private属性的name
public int Age; // 定义public属性的age
protected int Id; // 定义protected属性的id
public Student(string name, int age)
{
Name = name;
Age = age;
Id = 1;
}
}
在类似C#对权限要求比较高的语言中,如果要从外部访问private和protected属性,甚至都无法编译通过;而在python中,无法访问private属性,但是可以正常访问protected属性并且还能修改3
如果你曾经看过某些视频教程,其中还会教你怎么写以绕过private限制
print(student._Student__name)
# gcc
student._Student__name = "gcc1"
print(student._Student__name)
# gcc1
在python官方文档中,对于这个现象的说明是
那种仅限从一个对象内部访问的“私有”实例变量在 Python 中并不存在。 但是,大多数 Python 代码都遵循这样一个约定:带有一个下划线的名称 (例如 _spam) 应该被当作是 API 的非公有部分 (无论它是函数、方法或是数据成员)。 这应当被视为一个实现细节,可能不经通知即加以改变。
说的简单一点,python并没有提供那种非常严格的权限控制,更多的时候只是提供一种约定、协议,大家都遵照这样的写法,如果是非public属性就不要强行调用修改,而且根据目前python官方文档来看,似乎并没有严格意义上的protected属性,更多的还是一种约定俗成的作法。但如果你非要调用并修改,也不是不行,python并没有严格的做限制
关于private属性问题
如果你打算用反射调用某个对象内部的private属性的变量,你要格外注意了,你大概率会遇到报错,比如这样
class Student:
...
def report(self, text):
value = getattr(self, text)
print(value)
...
try:
student.report("__name")
except Exception as e:
print(e)
# 'Student' object has no attribute '__name'
此时只有改成_name,就不会出现报错了,有些迷惑
这大概也属于python的“灵活性”吧,如果你对这方面也比较在意的话,在对python做权限控制的时候需要好好考虑一下该如何设置,比如设置为只读不可写属性等,以尽可能避免被外部程序意外调用修改等