Python学习日志
RBHGO的主页欢迎关注
温馨提示:创作不易,如有转载,注明出处,感谢配合~
目录
前言
装饰器是一种Python中的特色语法,可以通过装饰器来增强现有的类或函数,这是一种非常有用的编程技巧。因为它在函数和类中都有实现,我会在这片日志分享中进行介绍。
进入正题
面向对象编程是一种非常流行的编程范式(programming paradigm),所谓编程范式就是程序设计的方法学,也就是程序员对程序的认知和理解。
前面的分享中我们说过“程序是指令的集合”,运行程序时,程序中的语句会变成一条或多条指令,然后由CPU(中央处理器)去执行。为了简化程序的设计,我们又讲到了函数,把相对独立且经常重复使用的代码放置到函数中,在需要使用这些代码的时候调用函数即可。如果一个函数的功能过于复杂和臃肿,我们又可以进一步将函数进一步拆分为多个子函数来降低系统的复杂性。
Python学习日志11课 - 面对对象编程
"""
Python学习日志11课 - 面向对象的编程
指令式编程 ---> 面向过程(函数)编程 ---> 适用于程序比较简单
编程范式(程序设计的方法论)︰面向对象编程 / 函数式编程
对象:对象是可以接收消息的实体,面向对象编程就是通过给对象发消息达到解决问题的目标。
- 对象 = 数据+方法(函数) ---> 对象将数据和操作数据的函数从逻辑上变成了一个整体
类:将一大类对象共同的特征(静态特征和动态特征)抽取出来之后得到的一个抽象概念。简单的说,
类是对象的蓝图(模板),有了类才能够创建出这种类型的对象。
面向对象编程
- 1.定义类型
~ 类的命名使用驼峰命名法(每个单词首字母大写)
~ 数据抽象:找到和对象相关的静态特征(属性)
~ 行为抽象:找到和对象相关的动态特征(方法)
- 2.创造对象
~ —切皆为对象
~ 对象都有属性和行为
~ 每个对象都是独一无二的
~ 对象—定属于某个类
- 3.发送消息
Author: RBHGO
Declaration: Mia San Mia ~~~
"""
一.定义类
在Python中,可以使用class 关键字加上类名来定义类,通过缩进我们可以确定类的代码块,就如同定义函数那样。写在类里面的函数除了必须的属性另外的我们通常称之为方法,方法就是对象的行为,也就是对象可以接收的消息。方法的第一个参数通常都是self ,它代表了接收这个消息的对象本身。
__init__ 方法通常也被称为初始化方法,在我们调用Student 类的构造器创建对象时,首先会在内存中获得保存学生对象所需的内存空间,然后通过自动执行__init__ 方法,完成对内存的初始化操作,也就是把数据放到内存空间中。所以我们可以通过给Student 类添加__init__ 方法的方式为学生对象指定属性,同时完成对属性赋初始值的操作。
class Student:
def __init__(self, name, age):
self.name = name
self.age = age
def eat(self):
"""吃饭"""
print(f'{self.name}正在吃饭.')
def study(self, course_name):
"""学习"""
print(f'{self.name}正在学习{course_name}. ')
def play(self, game_name):
"""玩游戏"""
print(f'{self.name}正在玩{game_name}. ')
def watch_American_TV(self, TV_name):
"""看电影"""
if self.age < 18:
print(f'{self.name}未满16岁,只能看《小猪佩奇》.')
else:
print(f'{self.name}正在观看{TV_name}.')
二.创建对象
在我们定义好一个类之后,可以使用构造器语法来创建对象,代码如下所示。
"""
堆(heap) ---> 我们通过构造器语法创建的对象基本都在堆空间,而对象的引用通常是
放在栈上的,通过对象引用(变量)就可以访问到对象并向其发送消息
"""
stu1 = Student('GG_bond', 15)
stu2 = Student('RBHGO', 20)
三.给对象发消息
给对象发消息也称为调用对象的方法。
stu1.study('Python算法与程序设计')
stu1.eat()
stu2.play('GTA5')
stu2.watch_American_TV('权力的游戏')
stu1.watch_American_TV('越狱')
打印对象
在Python中,以两个下划线__ (读作dunder )开头和结尾的方法通常都是有特殊用途和意义的方法,我们一般称之为魔术方法或魔法方法。
"""
魔术(魔法)方法 ---> 以__开头以__结尾 ---> 有特殊用途和意义的方法
- __init__ ---> 初始化方法,在调用构造器语法创建对象的时候会被自动调用
~ (initialize -> 初始化)
- __str__ ---> 获得对象的字符串表示,在调用 print函数直接输出对象时会被自动调用
- __repr__ ---> 获得对象的字符串表示,把对象放到容器中,用 print直接输出容器时会自动调用
~ (representation -> 表述)
- __lt__ ---> 对应小于号运算符,自动比大小
~ (less than -> '<'运算符)
Author: RBHGO
"""
class Card:
"""牌"""
def __init__(self, suite, face):
self.suite = suite
self.face = face
def __str__(self):
return self.display()
def __repr__(self):
return self.display()
def __lt__(self, other):
face1, face2 = self.face, other.face
return face1 < face2
def display(self):
"""显示牌"""
suites = {'S': '?', 'H': '?', 'C': '?', 'D': '?'}
faces = ['', 'A', '2', '3', '4', '5', '6', '7', '8', '9', '10', '7', 'Q', 'K']
return f'{suites[self.suite]}{faces[self.face]}'
def main():
"""程序的入口"""
card1 = Card('H', 5)
card2 = Card('S', 12)
card3 = Card('C', 9)
card4 = Card('D', 7)
print(card1.display(), card2.display())
print(card1, card2)
cardA = [card1, card2, card3, card4]
print(cardA)
if __name__ == '__main__':
main()
装饰器
装饰器是Python中用一个函数装饰另外一个函数或类并为其提供额外功能的语法现象。装饰器本身是一个函数,它的参数是被装饰的函数或类,它的返回值是一个带有装饰功能的函数。很显然,装饰器是一个高阶函数,它的参数和返回值都是函数。
我们来做一个可用装饰器的题定义描述三角形的类,提供计算周长和面积的方法。
"""
我们在类里面写的函数,通常称之为方法,它们基本上都是发给对象的消息。
但是有的时候,我们的消息并不想发给对象,而是希望发给这个类(类本身也是一个对象),
这个时候,我们可以使用静态方法或类方法。
静态方法 - 发给类的消息 ---> @staticmethod ---> 装饰器
类方法 - 发给类的消息 ---> @classmethod ---> 装饰器 ---> 第一个参数(cls)是接收消息的类
Author: RBHGO
"""
class Triangle:
def __init__(self, a, b, c):
self.a = a
self.b = b
self.c = c
@staticmethod
def is_valid(a, b, c):
return a + b > c and b + c > a and a + c > b
def perimeter(self):
"""计算周长"""
return self.a + self.b + self.c
def area(self):
"""计算面积"""
p = self.perimeter() / 2
return (p * (p - self.a) * (p - self.b) * (p - self.c)) ** 0.5
if __name__ == '__main__':
if Triangle.is_valid(1, 4, 5):
t = Triangle(3, 4, 5)
print(t.perimeter())
print(t.area())
else:
print('无效的边长,无法构造三角形对象')
我们在类中进行判断的时候,首先类没有完全建立,也就是说里面的函数还是面对对象,在面对对象都无法完成时,明显类无法建立,自然值也传不到类上面,所以我们要用装饰器来解决这个问题;将判断是否能构造三角形的问题控制到发送给对象,这样避免了程序不能顺利运行的尴尬。
小练
1.通过面对对象的方法,计算过道和围墙的造价。
import math
class Circle:
def __init__(self, radius):
self.radius = radius
def perimeter(self):
"""计算周长"""
return 2 * math.pi * self.radius
def area(self):
"""计算面积"""
return math.pi * self.radius ** 2
if __name__ == '__main__':
r = float(input('请输入游泳池的半径: '))
c1, c2 = Circle(r), Circle(r + 3)
fence_price = c2.perimeter() * 38.5
aisle_price = ((c2.area()-c1.area()) * 58.5)
print(f'围墙的造价是{fence_price:.2f}元')
print(f'过道的造价是{aisle_price:.2f}元')
2.定义一个时钟类(要让钟走起来)
import time
class Clock(object):
"""数字时钟"""
def __init__(self, hour=0, minute=0, second=0):
"""初始化方法
:param hour: 小时
:param minute: 分种
:param second: 秒种
"""
self.hour = hour
self.minute = minute
self.second = second
def run(self):
"""时间开始走"""
self.second += 1
if self.second == 60:
self.second = 0
self.minute += 1
if self.minute == 60:
self.minute = 0
self.hour += 1
if self.hour == 24:
self.hour = 0
def show(self):
"""显示时间"""
return f'{self.hour:0>2d}:{self.minute:0>2d}:{self.second:0>2d}'
clock = Clock(11, 46, 10)
while True:
print(clock.show())
time.sleep(1)
clock.run()
面对对象的四大支柱
面向对象编程的四大支柱:
- 抽象(abstraction): 提取共性(定义类就是一个抽象过程,需要做数据抽象和行为抽象)。
- 封装(encapsulation): 把数据和操作数据的函数从逻辑上组装成一个整体(对象)。
- 继承(inheritance): 扩展已有的类创建新类,实现对已有类的代码复用。
- 多态(polymorphism): 给不同的对象发出同样的消息,不同的对象执行了不同的行为。
(1).两个类之间的关系
"""
- is-a关系:继承 ---> 一个类派生出另一个类
class Teacher(Person)
- has-a关系:关联 ---> 把一个类的对象作为另外一个类的对象的属性
a car has an engine.
~ (普通)关联:
~ 强关联:整体和部分的关系 ---> 聚合(整体对部分的生命周期不负责任)和合成(整体对部分的生命周期负责任)
- use-a关系∶依赖 ---> —个类的对象作为另外—个类的方法的参数或返回值
a person use a vehicle
Author: RBHGO
"""
class Vehicle:
"""交通工具"""
pass
class Horse(Vehicle):
"""马"""
pass
class Motobike(Vehicle):
"""摩托车"""
def __init__(self):
self.engine = Engine()
class Engine:
"""引擎"""
pass
class Follower:
"""徒弟"""
pass
class MrTang:
"""唐僧"""
def __init__(self):
self.followers = []
def drive(self, vehicle):
"""驾驶"""
pass
(2).继承
对于打印对象中牌类的问题,如果我们想按照黑桃梅方的顺序整理手牌,那么或许通过继承内置函数enum 枚举时最好的方法。
"""
枚举
如果一个变量的取值只有有限个选项,可以考虑使用枚举类型。
Python中没有定义枚举类型的语法,但是可以通过继承Enum类来实现枚举类型。
- 结论1:枚举类型是定义符号常量的最佳选择! ! !
- 结论2:符号常量(有意义的名字)总是优于字面常量! ! !
Author: RBHGO
"""
from enum import Enum
class Suite(Enum):
SPADE, HEART, CLUB, DIAMOND = range(4)
class Card:
"""牌"""
def __init__(self, suite, face):
self.suite = suite
self.face = face
def __str__(self):
return self.show()
def __repr__(self):
return self.show()
def __lt__(self, other):
if self.suite == other.suite:
return self.face < other.face
return self.suite.value < other.suite.value
def show(self):
"""显示"""
suites = ['?', '?', '?', '?']
faces = ['', 'A', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K']
return f'{suites[self.suite.value]}{faces[self.face]}'
def main():
"""程序入口"""
card1 = Card(Suite.HEART, 1)
card2 = Card(Suite.SPADE, 13)
print(card1, card2)
print(card1 is card2)
card3 = Card(Suite.DIAMOND, 9)
card4 = Card(Suite.CLUB, 11)
print(card3.show(), card4.show())
card1 = card2
print(card1, card2, card3)
print(card1 is card2)
print(card1 is card3)
cards = [card1, card2, card3, card4]
print(cards)
if __name__ == '__main__':
main()
继承的语法是在定义类的时候,在类名后的圆括号中指定当前类的父类。如果定义一个类的时候没有指定它的父类是谁,那么默认的父类是object 类。object 类是Python中的顶级类,这也就意味着所有的类都是它的子类,要么直接继承它,要么间接继承它。下面是对继承详细的介绍和应用:
"""
继承:对已有的类进行扩展创建出新的类,这个过程就叫继承。
提供继承信息的类叫做父类(超类、基类),得到继承信息的类称为子类(派生类)。
Python中的继承允许多重继承,一个类可以有一个或多个父类。
建议如果不是必须使用多重继承的场景下,请尽量使用单一继承。
如果使用了多重继承的场景应该尽量避免出现`菱形继承`的情况,否则代码的可读性和可理解性都会变得非常的糟糕。
继承是实现代码复用的一种手段,但是千万不要滥用继承。
- 继承是—种is-a关系。
~ a student is a person.
~ a teacher is a person.
~ a programmer is a person.
子类直接从父类继承公共的属性和行为,再添加自己特有的属性和行为,
所以子类一定是比父类更强大的,任何时候都可以用子类对象去替代父类对象。
super() ---> super()函数在子类中调用父类的初始化方法
Author: RBHGO
"""
class Person:
"""人"""
def __init__(self, name, age):
self.name = name
self.age = age
def eat(self):
"""吃饭"""
print(f'{self.name}正在吃饭.')
def study(self, course_name):
"""学习"""
print(f'{self.name}正在学习{course_name}. ')
def play(self, game_name):
"""玩游戏"""
print(f'{self.name}正在玩{game_name}. ')
def watch_American_TV(self, TV_name):
"""看电影"""
if self.age < 18:
print(f'{self.name}未满16岁,只能看《小猪佩奇》.')
else:
print(f'{self.name}正在观看{TV_name}.')
class Teacher(Person):
def __init__(self, name, age, sex, tetel):
self.name = name
self.age = age
self.sex = sex
self.tetel = tetel
def teach(self, course_name):
"""教学"""
print(f'{self.tetel}{self.name}正在讲授{course_name}. ')
class Programmer(Person):
"""程序员"""
def __init__(self, name, age, sex):
super().__init__(name, age)
self.sex = sex
def write_cede(self, programming_lauguage):
"""写代码"""
print(f'{self.age}{self.sex}{self.name}正在用{programming_lauguage}写代码. ')
def main():
tcr1 = Teacher('金轮', 38, 'male', '金牌讲师')
tcr2 = Teacher('张三', 31, 'male', '法外狂徒')
tcr1.teach('《母猪的产后护理》')
tcr2.play('人类一败涂地')
tcr2.teach('《吸烟有害身体健康》')
tcr1.watch_American_TV('《斯巴达克斯》')
prog1024 = Programmer('RBHGO', 20, '男性')
prog1024.write_cede('Python3')
prog1024.eat()
if __name__ == '__main__':
main()
(3).多态
下面是对多态应用的例子:
"""
例子 ---> 工资(月薪)结算系统
三类员工:
~ 部门经理:固定月薪,15000元
~ 程序员:计时结算月薪,每小时200元
~ 销售员:底薪 + 提成,底薪1800元,销售额 5%提成
录入员工信息,自动结算月薪
子类对父类已有的方法,重新给出自己的实现版本,这个过程叫做方法重写(override)。
重写方法的过程中,不同的子类可以对父类的同一个方法给出不同的实现版本,那么该方法在运行时就会表现出多态行为。
多态(polymorphism):给不同的对象发出同样的消息,不同的对象执行了不同的行为。
面向对象编程的四大支柱:
~ 抽象(abstraction): 提取共性(定义类就是一个抽象过程,需要做数据抽象和行为抽象)。
~ 封装(encapsulation): 把数据和操作数据的函数从逻辑上组装成一个整体(对象)。
- 隐藏实现细节,暴露简单的调用接口。
~ 继承(inheritance): 扩展已有的类创建新类,实现对已有类的代码复用。
~ 多态(polymorphism): 给不同的对象发出同样的消息,不同的对象执行了不同的行为。
Author: RBHGO
"""
from abc import abstractmethod
class Employee:
"""雇员"""
def __init__(self, no, name):
self.name = name
self.no = no
@abstractmethod
def get_salary(self):
"""获得月薪"""
pass
class Manager(Employee):
"""部门经理"""
def __init__(self, name, no):
super().__init__(name, no)
def manage(self):
"""管理部门"""
pass
def get_salary(self):
return 15000
class Programmer(Employee):
"""程序员"""
def __init__(self, name, no):
super().__init__(name, no)
self.working_hour = 240
def write_code(self):
"""敲代码"""
pass
def get_salary(self):
return 200 * self.working_hour
class Salesman(Employee):
"""销售员"""
def __init__(self, name, no):
super().__init__(name, no)
self.sales_volume = 0
def product_sales(self):
"""产品销售"""
pass
def get_salary(self):
return 1800 + 0.05 * self.sales_volume
def main():
emps = [
Manager(3091, '大傻'),
Programmer(3082, '二麻'),
Salesman(3018, '李美丽')
]
for emp in emps:
if type(emp) == Programmer:
emp.working_hour = int(input(f'请输入{emp.name}本月工作时长:'))
elif type(emp) == Salesman:
emp.sales_volume = float(input(f'请输入{emp.name}本月销售额:'))
print(f' {emp.name}本月工资:{emp.get_salary()}元')
if __name__ == '__main__':
main()
总结
面向对象编程是一种非常流行的编程范式,除此之外还有指令式编程、函数式编程等编程范式。由于现实世界是由对象构成的,而对象是可以接收消息的实体,所以面向对象编程更符合人类正常的思维习惯。但是要想灵活运用面向对象编程中的抽象、封装、继承、多态需要长时间的积累和沉淀。Python是动态语言,Python中的对象可以动态的添加属性。在面向对象的世界中,一切皆为对象,我们定义的类也是对象,所以类也可以接收消息,对应的方法是类方法或静态方法。通过继承,我们可以从已有的类创建新类,实现对已有类代码的复用。
感谢学习陪伴,您的点赞,评论就是我更新的动力
|