Python学习笔记--类简介,继承,私有成员

时间:2021-05-10 00:32:49

简介:

        Python中也有类的概念,且与所有面向对象语言类似(更类似于java,而不是C++),只是没有public, private, protected关键字。Python使用自己的方法支持这些关键字的功能,但需要用户自己遵守,因为并不是严格支持。

        首先,从一个简单的例子学习Python中类的构成:(Python3.3)

class Dog:
    def __init__(self):
        self.nr=1# self.nr+=1 is wrong
        #Dog.nr=111
    def __del__(self):
        pass

class Cat:
    nr=0
    def __init__(self):
        self.nr=9# or self.nr+=9
        Cat.nr+=1
    
dog1=Dog()
print(dog1.nr)
#print(Dog.nr)

cat1=Cat()
print("cat1.nr=",cat1.nr,", Cat.nr=",Cat.nr,sep='')
cat2=Cat()
print("cat2.nr=%d, Cat.nr=%d"%(cat2.nr,Cat.nr))

        上例中,定义了两个类Dog与Cat,并分别定义了两个函数__init__(), __del__(),这两个函数是系统定义的函数(形如__func__的均是系统定义的函数,前后各两个下划线),分别表示构造函数与析构函数,且均可以省略不写。定义类方法时,第一个参数必须写,建议写self。 

        关于类中出现的的变量,简单的来说,比如在Cat类中,有self.nr与Cat.nr两种,他们的区别是self.nr是类实例的变量,而Cat.nr是类的变量。而在Dog类中由于没有给Dog.nr赋过值,即没有Dog.nr,则在类外面不能使用Dog.nr。同时,另一点需要注意的是:init函数中,如果self.nr是出现在=右边,则要保证其已经被赋过值,或者类名.nr这个变量有值。因为,当遇到+=等操作需要使用self.nr时,如果在函数内未定义过self.nr(即未使用=对self.nr赋过值),则会到函数外面的类变量中进行寻找,如果找到则将类的nr变量的值视为实例自己的nr变量的初值,然后再对类实例自己的nr赋值。以下Dog1是正确的,而Dog2错误:

class Dog1:
def __init__(self):
Dog.nr=111
self.nr+=1

class Dog2:
def __init__(self):
self.nr+=1
Dog.nr=111

        实际上,类变量的这种情况在继承中也有体现:如果子类中某一变量未被使用=赋过值,则会往上寻找到父类中调用这一变量。

继承,多态:

class Animal:
nr=0
def __init__(self):
self.nr+=1
Animal.nr+=1
def __del__(self):
pass
def act(self):
print("Act.")
def sleep(self):
print("Sleep.")

class Bird(Animal):

def __init__(self):
self.nr+=1
Bird.nr+=1
def act(self):
print("Fly.")

bird1=Bird()
print(bird1.nr,Bird.nr)
print(bird1.act())
bird1.sleep()

bird2=Bird()
print(bird2.nr)
print(bird1.nr,bird2.nr,Animal.nr,Bird.nr)

animal1=Animal()
print("Animal.nr=",Animal.nr)

        Bird是Animal的子类,使用形如SubClass(Class)来定义。子类可继承父类的所有公共的数据(包括变量与方法),并且可以使用同名的方法覆盖(overrid)父类的方法(即多态)。上例中,Bird继承了Animal的nr变量(当然Bird.nr与Animal.nr的值需要接下来详细讨论一下),及所有方法,但又自己重写了初始化函数与act()函数。所以当运行bird1.act()时,调用的是Bird类中的act(),而在bird1.sleep()中,调用的却是Animal.sleep()函数。

        在Bird的init()中,Bird.nr+=1实际上等价于Bird.nr=Animal.nr+1,self.in+=1也等价于self.nr=Animal.nr+1,因为之前未对Bird.nr赋过值,所以在Bird类中找不到nr变量时,会在其父类中查找,如果仍找不到才报错,self.nr同理,只是一个是类的变量,一个是类实例的变量。一旦给Bird.nr赋过值后,Bird.nr就有了自己的值,而不会是Animal.nr。下面这个Bird类,由于始终未对Bird.nr赋值,只是使用了其值,所以Bird.nr实际上一直是Animal.nr,并没有新分配内存来存,可以在改变Animal.nr后查看Animal.nr与Bird.nr进行验证。

class Bird(Animal):

def __init__(self):
Animal.__init__(self)
self.nr=Bird.nr+1
def act(self):
print("Fly.",Bird.nr)

        另外,Python不支持诸如C++, Java的根据参数类型与数目的不同而进行的重载(overload),即只以函数名字做为函数身份的判断,不会依据参数。而且如果出现多个同名函数(不论参数是否数目相同),则以最后一个为准(即后出现的函数将之前的同名函数覆盖)。

私有成员:

        Python并没有public, private, protected这些关键字,那么Python如何实现这些功能呢?

        实际上,Python使用了自己独特的方法实现的,如果函数名如下任意一种形式,则表示会具有特殊的含义:

        _func: 相当于protected func,但只有在某一函数写在类的外面的时候,才不能通过from module import *而被其他模块得到,其他情况均可以被得到。

        __func__: 表示系统定义的函数,所以尽量避免使用这种前后各两个下划线的方法定义用户自己的函数。

        __func: 相当于private func,但并非绝对的private,会通过下面一个例子解释其原理。

class Parent:
def __init__(self):
# print('a')
self.__act()
self.sleep()
def __act(self):
print("Parent acts.")
def sleep(self):
print("Parent sleeps.")

class Child(Parent):
def __act(self):
print("Child acts.")
def sleep(self):
print("Child sleeps.")


c=Child()

        运行结果为:

Parent acts.
Child sleeps.

        可以看到,这里Parent.__act()函数并没有被覆盖,这是因为在Python中,对形如__开头的函数会做name mangling,即将__func()变为_ClassName__func(),所以被处理之后的self.__act()变为了self._Parent__act(),而且def __act(self)变为了def _Parent__act(self),可以通过改变init函数来验证:将self.__act()显式的改为self._Parent__act(),不用改变函数定义。

        所以,虽然Python提供了一些类似这些关键字的技术,但并非绝对安全,主要还是需要用户自己遵守,不去破坏。