day18
1.静态方法和类方法
"""
之前我们在类中定义的方法都是对象方法,换句话说这些方法都是对象可以接收的消息。除了对象方法外,类中还可以有静态方法和类方法,者两类方法时发给类的消息,二者并没有实质性的区别,在面向对象的世界里,一切皆为对象,我们定义的每一个类其实就是一个对象,而静态方法和类方法就是发送给类对象的消息
"""
import math
# 定义类,描述平面上的点,提供移动点,计算一个点到另一个点的距离的方法
class Point:
"""点"""
def __init__(self, x, y):
"""
计算两点之间的距离
:param other: 另外一个点的坐标
:return: 两点间的距离
"""
self.x = x
self.y = y
def distance(self, other):
return math.sqrt((self.x - other.x) ** 2 + (self.y - other.y) ** 2)
def move_to(self, x, y):
"""
移动点
:param x:移动到那点的横坐标
:param y: 移动到那点的纵坐标
:return: None
"""
self.x = x
self.y = y
def move_by(self, dx, dy):
"""
偏移
:param dx: 横坐标的偏移量
:param dy: 纵坐标的偏移量
:return: None
"""
self.x += bx
self.y += by
def __repr__(self):
return f'<{self.x}, {self.y}>'
# 定义类描述平面上的线段,提供计算线段的长度,判断一个线段与另一个线段是否相交
class Line:
"""线段"""
def __init__(self, start:Point, end:Point):
"""
初始化方法
:param start: 开始点
:param end: 结束点
"""
self.start = start
self.end = end
def length(self):
"""
计算线段长度
:return: 线段长度
"""
return self.start.distance(self.end)
def intersected(self, other):
"""
判断两线段是否相交
:param other: 另一条线段
:return: 两线段是否相交
"""
sx1,sy1,ex1,ey1 = self.start.x, self.start.y, self.end.x, self.end.y
sx2,sy2,ex2,ey2 = other.start.x, other.start.y,other.end.x,self.end.y
# 德摩根率来判断,当一条线段的最大的横坐标小于另一条线段最小的横坐标,或者一条线段的的最大的纵坐标小于另一条线段的最小的纵坐标,这两条线段一定不相交
return not(
max(sx1, ex1) < min(sx2, ex2) or
max(sx2, ex2) < min(sx1, ex1) or
max(sy1, ey1) < min(sy2, ey2) or
max(sy2, ey2) < min(sy1, ey1)
)
# 定义三角形类,判断三条线是否能组成三角形,求面积方法
# 静态方法:不是三角形对象的消息,而是发给三角形类的消息 - @staticmethod
# Triangle.is_triangle()
# 类方法:发给类的消息(比静态方法多一个参数,代表接收消息的类) - @classmethod
class Triangle:
def __init__(self, line1:Line, line2:Line, line3:Line):
self.line1 = line1
self.line2 = line2
self.line3 = line3
# 发给类的消息
@classmethod
def is_valid_sides(cls, line1, line2, line3):
return line3+line2>line1 and line1+line3>line2 and line1+line2>line3
@staticmethod
def is_triangle(line1, line2, line3):
return line3+line2>line1 and line1+line3>line2 and line1+line2>line3
def perimerter(self):
p = self.line1 + self.line2 + self.line3
return p
def area(self):
a, b, c = self.line1, self.line2, self.line3
p = (a+b+c)/2
return math.sqrt(p(p-a)(p-b)(p-c))
p1 = Point(3, 5)
p2 = Point(6, 1)
print(p1, p2)
print(f'两点距离:{p1.distance(p2):.2f}')
p1.move_to(0, 0)
p2.move_by(1, -1)
print(p1, p2)
print(p1.distance(p2))
line1 = Line(p1, p2)
print(line1.length())
p3 = Point(-1, -2)
p4 = Point(6, 2)
line2 = Line(p3, p4)
print(line1.instersected(line2))
p3.move_to(4, 5)
print(line1.intersected(line2))
p1 = Point(0, 0)
p2 = Point(3, 0)
p3 = Point(3, 4)
l1 = Line(p1, p2).length()
l2 = Line(p1, p3).length()
l3 = Line(p2, p3).length()
s1 = Triangle(l1, l2, l3)
print(s1.perimeter())
def main():
line1, line2, line3 = map(float, input('请输入三条边的长度').split())
if Triangle.is_triangle(line1, line2, line3):
tri = Triangle(line1, line2, line3)
print(f'三角形的周长:{tri.perimeter()}')
print(f'三角形的面积:{tri.area()}')
else:
print('构不成三角形')
if __name__ == '__main__':
main()
上面的三角形类Triangle使用了staticmethod装饰器说明了is_triangle方法是Triangle类的静态方法,如果要声明类方法,可以使用classmethod装饰器。可以直接使用类名.方法名的方式来调用静态方法和类方法,二者的区别在于,类方法的第一个参数就是“类对象本身”,而静态方法则没有这个参数,总而言之,对象方法、类方法、静态方法都可以通过“类名.方法名”的方式来调用,区别在于方法的第一个参数到底是普通对象还是类对象,还是没有接受消息的对象。静态方法通常可以直接写成一个独立的函数,因为它并没有跟特定的对象绑定。
# 倒计时
import time
class CountDownClock:
"""倒计时"""
def __init(self, hour, minute, second):
"""
初始化方法
"""
self.hour = hour
self.minute = minute
self.second = second
def show(self):
"""显示时间"""
return f'{self.hour:0>2d}:{self.minute:0>2d}:{self.second:0>2d}'
def is_over(self):
"""判断倒计时是否结束"""
return self.hour == 0 and self.minute == 0 and self.second == 0
def run(self):
"倒计时"
if not self.is_over():
self.second -= 1
if self.second < 0:
self.minute -= 1
self.second = 59
if self.minute < 0:
self.hour -= 1
self.minute = 59
clock = CountDownClock(1, 0, 1)
print(clock.show())
while not clock.is_over:
print(clock.show())
time.sleep(1)
clock.run()
print('倒计时结束,时间到!')
注意:
# __repr__()方法
"""
通常情况下,我们经常直接输出类的实例化对象,例如:
class CLanguage:
pass
clangs = CLanguage()
print(clangs)
程序运行结果为:
<__main__.CLanguage object at 0x000001A7275221D0>
通常情况下,直接输出某个实例化对象,本意往往是想了解该对象的基本信息,
例如该对象有哪些属性,他们的值各是多少等等,但是在默认情况下,
我们得到的信息只会是”类命+object at+内存地址“,对我们了解实例化对象帮助不大
而我们可以通过”重写类__repr__()方法“就可以自定义输出实例化对象时的信息了,
事实上,当我们输出某个实例化对象时,其调用的就是该对象的__repr__()方法,输出的时该方法的返回值
其实上面的print(clangs)等同于执行print(clangs.__repr__()),程序的输出是一样的(地址可能不同)
和__init__(self)的性质一样,python中的每个类都包含__repr__()方法, 因为object类
包含__repr__()方法,而python中所有类都直接或间接继承自object类
默认情况下,__repr__()会返回和调用者有关的”类名+object at+内存地址“信息,当然,我们可以通过在类中重写这个方法,
从而实现当输出实例化对象时,输出我们想要的信息
例如:
class CLanguage:
def __init__(self):
self.name = 'C语言'
self.add = "http://c.biancheng.net"
def __repr__(self):
return f'CLanguage name={self.name}, add={self.add}'
clangs = CLanguage()
print(clangs)
程序运行结果为:
CLanguage name=C语言, add=http://c.biancheng.net
由此可见,__repr__() 方法是类的实例化对象用来做“自我介绍”的方法,
默认情况下,它会返回当前对象的“类名+object at+内存地址”,而如果对该方法进行重写,可以为其制作自定义的自我描述信息
"""
# 扑克游戏, 里面有那些类?对应的对象有哪些属性和方法
# 名词一般代表的时类和属性,动词是方法
import random
"""
类:牌,扑克,玩家
牌:属性:花色/点数
方法:显示牌面
扑克:属性:转54张牌的列表
方法:洗牌,发牌
玩家:属性:id , 昵称:手牌(列表)
方法:摸牌、打牌、整理手牌
"""
# 经验:符号常量总是优于字面常量!!!
# 通过将变量名大写告诉别人不要改它,表示常量
# space:黑桃 heart: 红心 club:梅花 diamond:方块
SPADE, HEART, CLUB, DIAMODN = range(4)
# 牌类
class Card:
def __init__(self, suite, face):
self.suite = suite
self.face = face
def __repr__(self): # 返回你想要看到的自定义的格式
return self.show()
# less than --> __lt__ ---> <
# great than --__gt__ ---> >
# equal to --> __eq__ --> ==
# great equal --> __ge__ --> >=
# less equal --> __le__ --> <=
def __lt__(self, other):
if self.suite != other.suite:
return self.suite < other.suite
face1 = 14 if self.face == 1 else self.face
face2 = 14 if other.face == 1 else other.face
return self.face < other.face
def show(self):
"""显示牌面"""
suites = ('♠', '♥', '♣', '♦')
faces = ('', 'A', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K')
return f'{suites[self.suite]}{faces[self.face]}'
class Poker:
"""扑克"""
def __init__(self):
self.cards = [Card(suite, face)
for suite in range(4)
for face in range(1, 14)]
self.index = 0
# 洗牌,随机乱序
def shuffle(self):
"""洗牌"""
self.index = 0
random.shuffle(self.cards)
# 判断是否还有牌
def has_more(self):
"""有没有牌可以发出"""
return self.index < len(self.cards)
# 发牌
def deal(self):
"""发牌"""
card = self.cards[self.index]
self.index += 1
return card
class Player:
"""玩家"""
def __init__(self, nickname):
"""
初始化方法
:param nickname: 昵称
"""
self.nickname = nickname
self.cards = []
def get_card(self, card):
"""摸牌"""
self.cards.append(card)
# 整理手牌第一种方法
def arrange(self):
"""整理手牌"""
# 第一种:列表的sort方法可以传入Key参数,来指定根据什么比较元素的大小
# self.cards.sort(key=lambda x: x.face) # 比较的是牌的点数
# self.cards.sort(key=lambda x: x.suite) # 比较的是牌的花色
self.cards.sort()
poker = Poker()
poker.shuffle()
names = ('妲己', '狄仁杰', '赵云', '孙悟空') # 玩家昵称
players = [Player(name) for name in names]
for _ in range(13):
for player in players:
card = poker.deal()
player.get_card(card)
for player in players:
player.arrange()
print(player.nickname, end=':')
print(player.cards)
2.继承
继承:通过已有的类创建新的类,子类会得到父类的继承信息,父类提供继承信息(基类、超类),公共的部分放在父类中,特有的部分写在子类中,子类通过继承父类,得到那些公共的部分
单一继承:一个类只能有一个父亲,建议大家一个类只有一个父亲
# 学生:姓名/年龄/年级;吃饭/玩耍/学习
# 老师:姓名/年龄/职称;吃饭/玩耍/授课
class Person: # 父类
def __init__(self, name, age):
"""初始化方法
:param name: 姓名
:param age: 年龄
:param grade: 年级
"""
self.name = name
self.age = age
def eat(self):
"""吃饭"""
print(f'{self.name}正在吃饭.')
def play(self):
"""玩耍"""
print(f'{self.name}正在玩耍.')
# 子类:得到继承信息的(衍生类)
# python语言允许多重继承,一个类可以有多个父类
class Student(Person): # 子类(父类)
"""学生"""
def __init__(self, name, age, grade):
"""初始化方法
:param name: 姓名
:param age: 年龄
:param grade: 年级
"""
super().__init__(name, age) # 继承的固定写法
self.grade = grade
def study(self, course_name):
"""学习
:param course_name: 课程名称
"""
print(f'{self.name}正在学习{course_name}.')
# 单一继承:一个类只能有一个父类,建议大家一个类只有一个父类
# 公共的部分放在父类中,特有的部分写在子类中,子类通过继承父类,得到那些公共的部分,继承也是一种复用代码的方式
class Teacher(Person): # 继承:通过已有的类创建新的类 ,子类会得到父类的继承信息,父类提供继承信息的(基类、超类)
def __init__(self, name, age, title):
"""
初始化方法
:param name: 老师名字
:param age: 年龄
:param title: 职称
"""
super().__init__(name, age)
self.title = title
def teach(self, course_name):
"""授课
:param course_name: 课程名称
"""
print(f'{self.name}正在上课{course_name}.')
student = Student('王大锤', 18, '大一')
teacher = Teacher('络昊', 41, '教授')
student.play()
teacher.teach('python程序设计')