详解抽象之类

2021-05-21  本文已影响0人  Python百事通

相较于函数,类则是更高级别的抽象结构,类(Class)是面向对象程序设计(OOP,Object-Oriented Programming)实现信息封装的基础。类是一种用户定义类型,也称类类型。每个类包含数据说明和一组操作数据或传递消息的函数。类的实例称为对象,类的实质是一种数据类型。

类的定义

与其它面向对象编程语言类似,在 Python 中,类具有多态、封装、继承。不过,Python 中没有重载,类的定义细节也具有明显差异。定义类的一般形式如下:

class ClassName:

<statement-1>

.

.

.

<statement-N>

上面提到,类的本质是一种数据结构,一个类通常包含数据成员和函数成员。数据成员用于刻画类所描述的一类事物的属性,如描述人,一般用姓名、年龄、性别、学历等属性进行刻画,这就是数据成员;函数成员用于完成具体的任务,如查询、设置人名、打印基本信息等。如下实例:

#定义一个简单的类,描述一个人的基本信息 

classPerson:

#定义类的数据成员:姓名,年龄

name=''

age=0

#定义一个函数:打印类实例的基本信息 

defprintPersonInfo(self):

print('person-info:{name:%s, age:%d}'%(self.name,self.age))

#定义一个简单的函数

defhello(self):

print("hello world!")

#实例化,创建一个对象

p1 = Person()

#访问类的属性:数据成员,访问语法obj.X

print("name:",p1.name)

print("age:",p1.age)

#访问类的函数

p1.printPersonInfo()

p1.hello()

执行结果:

name: 

age: 0

person-info:{name:, age:0}

helloworld!

self 参数

上述实例中,Person 类定义了两个函数,其定义形式与上一篇介绍的函数存在明显区别:类中的函数必须有一个额外的参数 self,并且 self 参数必须放在第一个参数的位置。

那么,对于一个实例化的对象,self 参数代表什么呢?来看一个例子。

#定义一个简单的类,描述一个人的基本信息 

classPerson:

#定义类的数据成员:姓名,年龄

name=''

age=0

#定义一个函数:打印类实例的基本信息 

defprintPersonInfo(self):

print('name:',self.name)

print('self:',self)

print('self class:',self.__class__)

#实例化,创建一个对象

p1 = Person()

#访问类的函数

p1.printPersonInfo()

执行结果:

name:

self:<__main__.Person object at 0x00000000067B5F98>

selfclass:

从执行结果可以看出,self 的内容是一个地址,它代表当前实例,也就是当前对象的地址。需要说明的是,self 参数并不是 Python 的保留关键字,而是为了便于理解,按照惯例命名而来。事实上,换做其它名字也可以(须遵循规则:必须是类函数的第一个参数)。

实例化

上面小节实例中,我们创建了一个 Person 类的对象:p1=Person(),通过对象可以访问类的属性和调用类的函数,语法形式为:obj.name,其中 name 代表类的属性名或函数名。

上述例子中存在一个疑点,不知读者是否注意到,例子中实例化对象的操作并不是显式调用构造函数完成的,如下代码:

p1= Person()

类中并没有定义名为 Person() 的函数,Person 是类名,在进行实例化创建对象的时候,会自动调用__init()__函数。该函数用于创建对象,并赋予所创建对象初始状态。

上述例子中,做了很多简化,创建的对象的所有属性都是默认值,在实际应用中,通常会采取更有效的方式来赋予对象初始状态。如下实例:

#定义一个简单的类,描述一个人的基本信息 

classPerson:

#定义类的数据成员:姓名,年龄

name=''

age=0

#定义构造函数,用于创建一个类实例,也就是类的具体对象

#通过参数传递,可以赋予对象初始状态

def__init__(self,name,age):

self.name = name

self.age = age 

#定义一个函数:打印类实例的基本信息 

defprintPersonInfo(self):

print('person-info:{name:%s, age:%d}'%(self.name,self.age))

#实例化,创建两个对象,默认调用构造函数:__init__()

p1 = Person("Zhang San",12)

p2 = Person("Li Si",13)

#访问类的属性:数据成员,访问语法obj.X

print("name:",p1.name)

print("age:",p1.age)

#调用函数

p1.printPersonInfo()

p2.printPersonInfo()

运行结果:

name: Zhang San

age: 12

person-info:{name:Zhang San, age:12}

person-info:{name:Li Si, age:13}

Python 中类定义与 Java、C++ 的差别

从上面的例子中可以发现,Python 中类的定义与 Java 和 C++ 的区别:

定义形式,Python 没有修饰符,只有关键词 class,Java 和 C++ 则有修饰符(非必须);

构造函数,Python 没有重载特性,只能定义一个构造函数,且函数名为__init__,若不定义构造函数,则默认为__init__(self),Java、C++ 则具有重载特性,可定义多个构造函数,且构造函数名必须与类名一致;

形参定义形式不同,Python 类的方法,self 参数为必须参数;

不必声明域,上面的例子中,声明了域 (name,age),事实上,Python 可以不声明域,例子如下

#定义一个简单的类,描述一个人的基本信息 

classPerson:

#定义构造函数,用于创建一个类实例,也就是类的具体对象

def__init__(self,name,age):

self.name = name

self.age = age 

#定义一个函数:打印类实例的基本信息 

defprintPersonInfo(self):

print('person-info:{name:%s, age:%d}'%(self.name,self.age))

继承

继承可谓一种带有褒义的懒惰行为,一个最直观的好处就是减少编写重复代码,通过继承,子类可以重用父类中的函数和数据成员。当然,继承的意义远不止于此,这里就不展开了。

关于继承,通常将实施继承行为的类称为子类(Child Class)或者派生类(Derived Class),被继承的类称为父类(Parent Class)或者基类(Base Class)。与 Java、C++ 相比,Python 中继承的一般形式颇为简洁:

classchildClassName(parentClassName):

.

.

.

下面结合实例来看一下,定义一个类 Occupation 和一个继承 Occupation 的类 Person,继承的定义形式为:Person(Occupation),无需关键词声明。

#定义一个类Occupation,描述职业

classOccupation:

#定义构造函数

def__init__(self,salary,industry):

self.salary = salary

self.industry = industry

defprintOccupationInfo(self):

print('Occupation-info:{salary:%d, industry:%s}'%(self.salary,self.industry)) 

#定义一个简单的类Person,继承自类Occupation 

classPerson(Occupation):

def__init__(self,name,age):

self.name = name

self.age = age 

#定义一个函数:打印类实例的基本信息 

defprintPersonInfo(self):

print('person-info:{name:%s, age:%d}'%(self.name,self.age))

#创建一个子类对象

temp = Person('Wu-Jing',38)

#访问父类的数据成员

temp.salary = 21000

temp.industry = "IT"

#分别调用本身和父类的函数

temp.printOccupationInfo()

temp.printPersonInfo()

执行结果:

Occupation-info:{salary:21000, industry:IT}

person-info:{name:Wu-Jing, age:38}

多继承

一些场景下,一个子类可能需要继承多个父类,

举个例子:有三个类分别描述职

业信息,购物信息,银行账户信息,现在定义一个类

Person来描述一个人,

显然,Person涉及上述三个类的信息,为了重复利用

代码,降低开发难度,可以直接继承上述三个类,这便是多继承的应用。

如上所述,

多继承定义形式如下:

classchildClassName(parentClassName1,parentClassName2,…):

.

.

.

关于多继承,实例如下:

#定义一个类BankAccount,描述银行账户

classBankAccount:

def__init__(self,number, balance):

self.number = number

self.balance = balance 

#计算并返回年利息

defgetAnnualInterest(self):

returnself.balance*0.042

#定义一个类Occupation,描述职业

classOccupation:

def__init__(self,salary,industry):

self.salary = salary

self.industry = industry

defprintOccupationInfo(self):

print('Occupation-info:{salary:%d, industry:%s}'%(self.salary,self.industry)) 

#定义一个类Person,继承自类BankAccount和BankAccount 

classPerson(Occupation,BankAccount):

def__init__(self,name,age):

self.name = name

self.age = age 

#定义一个函数:打印类实例的基本信息 

defprintPersonInfo(self):

print('person-info:{name:%s, age:%d}'%(self.name,self.age))

#创建一个子类对象

temp = Person('Wu-Jing',38)

#访问父类数据成员

temp.number = 622202050201

temp.balance = 1000000.99

temp.salary = 21000

temp.industry = "IT"

#分别调用本身和父类的函数

temp.printOccupationInfo()

temp.printPersonInfo()

print('Annual interest:',temp.getAnnualInterest())

执行结果:

Occupation-info:{salary:21000, industry:IT}

person-info:{name:Wu-Jing, age:38}

Annualinterest: 42000.041580000005

需要注意的是,多继承中,子类继承了不同父类中的属性和函数,这些属性和函数可能存在同名的情况,在子类使用这些同名的函数或属性时,在没有指定的情况下,Python 将根据一定顺序进行搜索:首先搜索子类,如果未找到则根据多继承定义的顺序,从左至右在父类中查找。

如下实例:

#定义一个类 BankAccount,描述银行账户

classBankAccount: 

defprintInfo(self):

print('BankAccount-info') 

#定义一个类 Occupation,描述职业

classOccupation:

defprintInfo(self):

print('Occupation-info') 

#定义一个类 Person,继承自类 BankAccount 和 BankAccount 

classPerson(Occupation,BankAccount):

def__init__(self,name,age):

self.name = name

self.age = age 

defprintPersonInfo(self):

print('person-info')

#创建一个子类对象

temp = Person('Wu-Jing',38)

#调用父类中的函数

temp.printInfo()

执行结果:

Occupation-info

很明显,根据定义顺序,优先调用父类 Occupation 中的 printInfo()。

函数的重写

一些场景下,从父类继承来的函数并不能完全满足需求,需要在子类中对其进行修改,这就是重写的概念:在子类中重写父类中的函数,当子类对象调用该名称的函数时,会调用子类中重写的函数,父类中的同名函数将被覆盖。

实例如下:

#定义一个类Occupation,描述职业

classOccupation:

#定义构造函数

def__init__(self,salary,industry):

self.salary = salary

self.industry = industry

defprintInfo(self):

print('salary:%d, industry:%s}'%(self.salary,self.industry)) 

#定义一个简单的类Person,继承自类Occupation 

classPerson(Occupation):

def__init__(self,name,age):

self.name = name

self.age = age 

#定义一个函数:打印类实例的基本信息 

defprintInfo(self):

print('name:%s, age:%d'%(self.name,self.age))

print('salary:%d, industry:%s'%(self.salary,self.industry))

#创建一个子类对象

temp = Person('Wu-Jing',38)

#访问父类的数据成员

temp.salary = 21000

temp.industry = "IT"

#分别调用函数printInfo()

temp.printInfo()

执行结果

name:Wu-Jing, age:38

salary:21000, industry:IT

私有属性与私有方法

前面几节的实例中,类的属性和函数都是“公有”的,可以通过类对象直接访问。

但是,在某些场景下,我们并不希望对外暴露类的内

部细节,为了限制外部访问,我们可以将对应的属性和函数设置为私有。

将类的属性和函数设置为私有的一般形

式为以下两种。

1.定义私有属性

__attribute:属性名前面加两个下划线,

即声明该属性为私有,不能在类的外部直接访问,

在类内部访问时用

 self.__attribute。

2.定义私有函数

__function:函数名前面加两个下划线,即声明该函数为私有,不能在类的外部直

接访问,在类内部访问时用 self.__ function。

实例如下:

#定义一个简单的类,描述一个人的基本信息 

classPerson:

#定义两个私有属性name,age

def__init__(self,name,age):

self.__name = name

self.__age = age 

#定义公有函数,在类外部可以访问 

defgetName(self):

self.__fun()

returnself.__name

defgetAge(self):

returnself.__age

#定义一个私有函数,只能在类内部使用

def__fun(self):

print('hello')

#实例化

p1 = Person("Zhang San",12)

#访问类的私有属性和私有函数,将会报错

print("name:",p1.__age)

print("age:",p1.__name)

p1.__fun()

对于私有属性和私有函数,如果需要在类外访问,可以通过公有函数实现,这与

Java 和 C++ 是一致的。

如下实例:

#定义一个简单的类,描述一个人的基本信息 

classPerson:

#定义两个私有属性name,age

def__init__(self,name,age):

self.__name = name

self.__age = age 

#定义公有函数,在类外部可以访问 

defgetName(self):

self.__fun()

returnself.__name

defgetAge(self):

returnself.__age

#定义一个私有函数,只能在类内部使用

def__fun(self):

print('hello')

#实例化

p1 = Person("Zhang San",12)

#访问类的公有函数

print("name:",p1.getName())

执行结果:

hello

name: Zhang San

上一篇下一篇

猜你喜欢

热点阅读