python中的类实例和访问限制
面向对象最重要的概念就是类(Class)和实例(Instance),类是抽象的模板,而实例是根据类创建出来的一个个具体的“对象”,每个对象都拥有相同的方法,但各自的数据都互相独立,互不影响。
定义类是通过class关键字:
class Myclass([object]): pass
class后面紧接着是类名Myclass,类名通常是大写开头的单词,object表示要继承的父类,一个类可以有多个父类,多个父类之间用逗号隔开,如果没有继承类,默认使用object类,这是所有类最终都会继承的类,object可以省略,你甚至可以省略掉括号在python中也是允许的class Myclass:。
创建实例是通过ClassName()实现的:o = Myclass()
给一个实例变量绑定新的或修改属性:o.name = ‘MyName‘
定义类的方法:
python严格要求方法需要有实例才能被调用,这种限制其实就是python所谓的绑定概念。
class C: def get_c(): print(‘i am c‘)
使用类对象直接调用C.get_c()正常打印
使用实例对象a = C()调用a.get_c()却报错"TypeError: get_c() takes 0 positional arguments but 1 was given"
class C: def get_c(self): print(‘i am c‘)
使用类对象直接调用C.get_c()报错"TypeError: get_c() missing 1 required positional argument: ‘self‘"
使用实例对象a = C()调用a.get_c()正常打印
和普通的函数相比,在类中定义的函数只有一点不同,就是第一个参数永远是变量self,表示创建的实例本身(把方法绑定到实例对象),并且调用时不用传递该参数。除此之外,类的方法和普通函数没有什么区别,所以,你仍然可以用默认参数、可变参数、关键字参数和命名关键字参数。
类对象和实例对象
class C: n = 0 x = C() y = C()
此时x.n、y.n和C.n都是0
我们y.n+=10
此时x.n和C.n是0,而y.n是10
我们再C.n+=100
此时x.n和C.n是100,而y.n仍然是10
访问限制
外部代码可以自由地修改一个实例的属性:o.age = 99,这样我们传入的值18就没有意义了,如果要让内部属性不被外部访问,可以把属性的名称前加上两个下划线__,在Python中,实例的变量名如果以__开头,就变成了一个私有变量(private),只有内部可以访问,外部不能访问,所以,我们把类改一改:
class Myclass: def __init__(self, name,age): self.__name = name self.__age = age def print_info(self): print(‘%s: %s‘ % (self.__name, self.__age))
改完后,对于外部代码来说,没什么变动,但是已经无法从外部访问实例变量的__name和__score了,只能通过print_info()方法来打印输出。这样就确保了外部代码不能随意修改对象内部的状态,这样通过访问限制的保护,代码更加健壮。
如果外部要获取__name怎么办?可以给类增加get_name这样的方法:
class Myclass: ... def get_name(self): return self.__name
同理如果要允许外部修改__name怎么办?可以再给类增加set_name方法。
你也许会问,原先那种直接通过o.age=20也可以修改啊,为什么要定义一个方法大费周折?因为在方法中,可以对参数做检查,避免传入危险的或者无效的值。
在Python中,变量名类似__xxx__的,也就是以双下划线开头,但是是以双下划线结尾的,是特殊变量,特殊变量是可以直接访问的,不是私有变量,所以,不能使用__name__、__score__这样的变量名。
以一个下划线开头的实例变量名,比如_name,这样的实例变量外部是可以访问的,但是,按照约定俗成的规定,当你看到这样的变量时,意思就是,“虽然我可以被访问,但是,请把我视为私有变量,不要随意访问”。
双下划线开头的实例变量是不是一定不能从外部访问呢?其实也不是。不能直接访问__name是因为Python解释器对外把__name变量转换成了_Myclass__name变量,所以,可以通过o._Myclass__name访问变量,但强烈建议不要这么干,因为不同版本的Python解释器可能会把__name改成不同的变量名。
总的来说就是,Python本身没有任何机制阻止你干坏事,一切全靠自觉。
最后注意下面的这种写法:
o.__name = ‘NewName‘
bart.__name
‘NewName‘
表面上看,外部代码“成功”地设置了__name变量,但实际上这个__name变量和class内部的__name变量不是一个变量!内部的__name变量已经被Python解释器自动改成了_Myclass__name,而这样操作仅仅是给o新增了一个__name变量 。
o.get_name()
‘MyName‘