文章目录
Python class
一、类的构造 & 初始化
通过 __init__
内置函数进行初始化:
class Student:
def __init__(self, name, age):
print('Start initialization...')
self.name = name
self.age = age
alice = Student('Alice', 20)
bob = student('Bob', 20)
# show address
print(id(alice)) # different address
print(id(bob))
# show dictionary of each student
print(alice.__dict__)
# {'name': 'alice', 'age': 20}
print(bob.__dict__)
# {'name': 'bob', 'age': 20}
通过__dict__
实现多字段初始化的代码优化:
class person:
'''
def __init__(self, name, age, hobby, address, email):
# intialization
# self.name = name
# ...
# self.email = email
'''
# using __dict__ to init
def __init__(self, info):
self.__dict__.update(info)
perinfos = open('person_info.txt', 'r')
persons = []
for p in perinfos:
info = p.split(', ')
persons.append(person(info))
print(persons)
二、类的特点
- 多态
Polymorphism
- 封装
Capsulation
- 继承
Inheritation
2.1 类的继承:Inheritation
2.1.1 继承的定义
###########################################################################################################
###########################################################################################################
### ___ ______ ____ ___ _______ _______ ___ ___
### | |\ | | | | | \ | | /\ | | / \ |\ |
### | | \ | |_____| |____ |____/ | | / \ | | | | | \ |
### | | \ | | | | | \ | | /----\ | | | | | \ |
### _|_ | \| | | |_____ | \ _|_ | / \ | _|_ \___/ | \|
###########################################################################################################
###########################################################################################################
# derived_class - Class this class derived from
class class_name(derived_class):
# some variables
# some override functions such as: __init__, __call__
# some user defined function
2.1.2 多重继承
Python 支持多重继承,子类会继承父类的所有变量和方法。变量搜索方式:
- 从下往上
- 从左往右
class parent1:
x = 'parent1 x'
parent_name = 'parent1'
class parent2:
x = 'parent2 x'
parent_name = 'parent2'
class child(parent1, parent2):
myname = 'myname is child'
child = child()
print(child.myname)
print(child.parent_name) # output: parent1 which is first in parent list
2.1.3 搜索方式
- 深度优先搜索(DFS, Depth-First-Search)
- 广度优先搜索(BFS, Broadth-First-Search)
Python2:经典类和新式类区别
经典类:
class A:
def echo(self):
print('I am A')
class B(A):
# do nothing
pass
class C:
def echo(self):
print('I am C')
class D(B,C):
pass
d = D()
print(d.echo()) # I am A
新式类:
class A(Object):
def echo(self):
print('I am A')
class B(A):
# do nothing
pass
class C:
def echo(self):
print('I am C')
class D(B,C):
pass
d = D()
print(d.echo()) # I am C
Python3:统一为广度优先搜索
广度搜索
2.1.4 变量和方法
通过利用显式调用父类中的同名函数,实现代码优化。
功能函数的继承相关
首先我们定义了三个类:
person
:基类,包含一个say
方法和一个增加薪水的 `staff
:根据普通员工的薪水公式,重写父类中的薪水方法manager
:同上,但是薪水计算方法不同
我们在每个类中都定义了不同的薪水计算公式,并且设置了相应的形式参数:
class person:
def __init__(self, name, age, salary):
self.name = name
self.age = age
self.salary = salary
def say(self):
s = 'My name is %s, my age is %s, and my price is %s' %(self.name, self.age, self.price)
def raise_salary(self, percentage):
self.salary = self.salary * (1 + percentage)
class staff(person):
def __init__(self):
pass
def raise_salary(self, percentage, performance):
self.salary = self.salary * (1 + percentage + performance)
class manager(person):
def __init__():
pass
def raise_salary(self, percentage, performance, shareoutbonus):
self.salary = self.salary * (1 + percentage + performance + shareoutbonus)
显然,此时当我们希望对薪水计算公式,或者是参数进行修改时,我们需要对每个函数都进行修改,但是通过在子类的功能函数中通过显式调用父类的功能函数来实现,便可以直接对父类函数中的方法进行修改即可:
# staff
def raise_salary(self, percentage, performance):
# self.salary = self.salary * (1 + percentage + performance)
person.raise_salary(percentage = percentage + performance)
# manager
def raise_salary(self, percentage, performance, shareoutbonus):
# self.salary = self.salary * (1 + percentage + performance + shareoutbonus)
person.raise_salary(percentage = percentage + performance + shareoutbonus)
构造函数的继承相关
当子类中添加了新的成员变量,并且在希望在构造过程中传递新的变量时:
## 构造函数的继承
class person:
def __init__(self, name, age, price):
self.name = name
self.age = age
self.price = price
class manager(person):
def __init__(self, address):
self.address = address
m2 = manager('m2', 20, 1000, 'Dongguan') # error 'cause __init__ has been overrrided
此时如果子类的初始化函数仅包含新的字段形参,而在构造的时候传递了包括父类初始化函数中的参数,报错,因为 __init
的方法是在子类中被完全重写,并且 Python 并不会为多余的参数隐式调用父类的初始化函数,因此需要完整的重写初始化函数,给出相应的每个字段的赋值语句:
## 构造函数的继承
class person:
def __init__(self, name, age, price):
self.name = name
self.age = age
self.price = price
class manager(person):
def __init__(self, name, age, price, address):
self.name = name
self.age = age
self.price = price
self.address = address
m2 = manager('m2', 20, 1000, 'Dongguan') # error 'cause __init__ has been overrrided
显然,此时子类和父类的初始化函数中存在较多的重复代码。
当子类的初始化函数中和父类存在大量重复的赋值语句时,可以通过显式调用父类的初始化函数来实现代码优化:
class person:
def __init__(self, name, age, price):
self.name = name
self.age = age
self.price = price
class manager(person):
def __init__(self, name, age, price, address):
self.address = address
'''
self.name = name
self.age = age
self.price = price
'''
# instead explicitly called person's initialization method
person.__init__(self, name, age, price)
m2 = manager('m2', 20, 1000, 'Dongguan') # pass
2.1.5 继承的应用场景
- 合成
- 聚合
- 复用
合成
部分和整体的生命周期一致,整体拥有对其组织部份的支配权,且一个成分只属于一个合成关系,不可共享
例如:
- 房子:包含房间s,房间s只属于这个房子,房子消失,房间消失,一种强拥有的关系
- 房间:房子内部,生命周期小于或者等于房子,房子消失必然伴随房间消失
class room:
def create_room(self):
print("this is a new room")
class house:
def __init__(self):
self.room = room()
def create_house(self):
self.room.create_room()
h1 = house()
h1.create_house()
聚合
表示一种拥有关系,整体与部分之间的关系
例如:
- 学生:部分,学校倒闭了,但是学生依然存在,不会因为整体的消失而消失,即部分的生命周期可以超过整体
班机班级:包含每个学生部分的整体
# 学生,教室的一部分
class student:
pass
# 教室,包含学生的整体
class classroom:
def __init__(self, student):
self.student = student
s1 = student()
c1 = classroom(s)
对象关系:
IS-A
:我是一个人(?),显然这是一种继承关系HAS-A
:人拥有脑子(?),显然是一种合成关系
例子:
m
a
n
{
i
n
c
o
m
p
a
n
y
:
e
m
p
l
o
y
e
e
a
t
h
o
m
e
:
h
u
s
b
a
n
d
f
a
t
h
e
r
a
t
s
c
h
o
o
l
:
s
t
u
d
e
n
t
man\begin{cases} in\ company:employee\\ at\ home:husband&father\\ at\ school:student \end{cases}
man⎩⎪⎨⎪⎧in company:employeeat home:husbandat school:studentfather
class person:
def show(self):
print('live a happy life')
class staff:
def show(self):
print("try to work hard")
class husband(person):
def show(self):
print("work and earn money")
class student(person):
def show(self):
pritn("good good study day day up")
p = staff()
p.show()
# different person, not able to create a person with different roles
p = husband()
p.show()
class role:
def show(self):
pass
class staff(role):
def show(self):
print("try to work hard")
class husband(role):
def show(self):
print("try best to make family happy")
class student(role):
def show(self):
pritn("good good study day day up")
class person:
def set_role(self, role):
self.role = role
def get_role(self):
self.role.show()
p = person()
p.set_role(staff)
p.get_role() # "try to work hard"
p.set_role(husband)
p.get_role() # "try best to make family happy"
p.set_role(student)
p.get_role() # "good good study day day up"
复用
l o a d i n g … loading\dots loading…
2.1.6 迪米特原则:最小知道原则
保证封装性,减少类的内部暴露,例子:
- A 类:被 B 依赖,只暴露必须提供的方法和属性,即定义好方法和属性的可见范围
- B 类:依赖于 A,只依赖应该依赖的对象,即只想需要提供信息的对象提供信息,不向无关的类暴露内容
例如:
class A:
def __init__(self, name):
self.name = name
def get_b(self, name):
return B(name)
# A -> B -> C to work
def work(self):
b = self.get_b('b')
c = b.get_c('c')
c.work()
class B:
def __init__(self, name):
self.name = name
def get_c(self, name):
return C(name)
class C:
def __init__(self, name):
self.name = name
def work(self):
print('Done! by' + self.name)
a = A('a')
a.work()
可以发现,此时在 A
中调用了 C
相关的代码,但是显然 C
应该是对 A
隐藏的,也就是说,我们应该利用 B
来让 C
完成相关的工作,而 A
只需要联系 B
即可:
class A:
def __init__(self, name):
self.name = name
def get_b(self, name):
return B(name)
# A -> B -> C to work
def work(self):
b = self.get_b('b')
# c = b.get_c('c')
# c.work()
b.work()
class B:
def __init__(self, name):
self.name = name
def get_c(self, name):
return C(name)
def work(self):
c = self.get_c('c')
c.work()
class C:
def __init__(self, name):
self.name = name
def work(self):
print('Done! by' + self.name)
a = A('a')
a.work()a.work()
此外,对于被依赖者来说,依赖者应该尽可能的减少内部具体的流程方法的暴露,而只向用户提供一个用户友好的并且细节隐藏的方法,既提高类的封装性,又能提高用户的使用体验:
class computer:
# private methods
def __stop_service(self):
print('wait for stopping process...done!')
def __poweroff(self):
print('wait for power off...done!')
def __shut_screen(self):
print('screen is going to sleep...done!')
def __save_data(self):
print('writing back data...done!')
# public
def shutdown(self):
self.__stop_service()
self.__save_data()
self.__shut_screen()
self.__poweroff()
if __name__ == '__main__':
c = computer()
c.shutdown()
# c.__save_data() error
# c.__stop_service() error
2.2 类的封装:Capsulation
########################################################################################################
########################################################################################################
### ___ ____ ___ _______ ___ ___ #####
### / \ /\ | \ / \ | | | /\ | | / \ |\ | #####
### | / \ |____/ \___ | | | / \ | | | | | \ | #####
### | /----\ | \ | | | /----\ | | | | | \ | #####
### \___/ / \ | \___/ |_____| |_____ / \ | _|_ \___/ | \| ###
############################################################################################################
############################################################################################################
首先,在 Python 中,封装只是一种约定,也就是说,事实上任何一个对象的内部变量都可以通过某种方式在外部访问。对于一个普通的变量来说,外部可以随意访问,比如:
class temp:
var = 1
# some definitions
这个变量 var
可以在外部随意访问。
不可访问类型,Python 约定了两种内部变量:
_varname
:变量名以下划线开头的变量__varname
:变量名以双下划线开头的变量
例如:
class temp:
var = 1
def __init__(self):
self._var = 2
self.__var = 3
# other definitions
t = temp()
print(t.var) # 1
print(t._var) # 2,依然可以访问,仅仅是约定
print(t.__var) # error,双下划线不可以访问
print(t._temp__var) # 3, 但是依然可以通过字典访问,进一步说明 Python 的私有仅仅只是约定,并没有完全限制访问
2.3 类的多态:Polymophism
class animal:
def run(self):
print('it is running')
class dog(animal):
def run(self):
print('dog is running')
class cat(animal):
def run(self):
print('cat is running')
if __name__ == '__main__':
d = dog()
d.run()
c = cat()
c.run()
print(isinstance(d, dog)) # True
print(isinstance(d, animal)) # True
print(isinstance(c, animal)) # True
print(isinstance(c, dog)) # False
三、抽象类
- 不能实例化(当存在抽象方法时)
- 通过函数装饰器
@abstractmethod
将抽象类中相应的功能函数设置为抽象函数,子类必须实现
from abc improt ABCMeta, abstractmethod
class animal(metaclass=ABCMeta):
def__init__(self, name, age):
self.name = name
self.age = age
@abstractmethod
def run(self):
pass
class dog(animal):
def __init__(self, name, age):
self.name = name
self.age = age
def run(self):
print('a dog runs with four legs running')
class kangroo(animal):
def __init__(self, name, age):
self.name = name
self.age = age
def run(self):
print('a kangroo runs with two legs jumping')
d = dog('dd', 3)
d.run()
k = kangroo('kk', 5)
k.run()
通过抽象类:
- 保证子类包含并且实现相应的功能
- 保证命名的规范