Python学习第三周总结
函数
基于第二周的总结,第三周在函数上多学习了可变参数、关键字参数、命名关键字参数
可变参数指的是在不确定要传入多少个数据的情况下,将函数的参数设置为(*args)这样函数其实是将你传入的所有数据组装成了一个元组。
关键字参数指的是允许你传入0个或任意个含参数名的参数,这些关键字参数在函数内部自动组装为一个字典。
命名关键字参数则是在第一个’*’号后的所有参数在赋值的时候都需要写上变量名
面向对象的编程
对象:在编程语言中一切皆为对象,特别是在Python中,所有东西都是一个对象,函数时一个对象,定义的一个变量也是一个对象,我们现实生活中的一个人,一辆车,一支笔都是对象。
类:类是对一类对象的特征和行为的一种抽象的抽取,类中的属性表示变量的静态特征,类中的方法表示变量的动态特征,举个栗子,如果一个学生对象我们抽取他的类,那么他的静态特征就是名字,学号,年龄,性别等,而动态特征就有比如上学,看电视等。
以上的东西上周的总结都是有的,看官们可以移步‘上周总结’
这周主要是在基本了解了面向对象的概念之后,学习了面向对象的继承、多态以及@property装饰器、静态方法等
@property装饰器
在Python中,如果我们要将对象的属性设为私有的,那么建议属性命名以下划线开头(虽然这样也没有什么卵用,Python并不支持真正的私有属性,而且我也觉得这样写太麻烦了)来暗示其他人这个属性是受保护的,不建议外部直接访问,这个时候如果想想访问属性可以通过属性的getter(访问器)和setter(修改器)方法进行对应的操作,使用@property包装器来包装getter和setter方法,使得对属性的访问既安全又方便,代码如下:
class Rectangle(object):
def __init__(self, lang, weight):
self._lang = lang
self._weight = weight
# 访问器 - getter方法
@property
def lang(self):
return self._lang
# 修改器 - setter方法
@lang.setter
def lang(self, lang):
self._lang = lang if 0 < lang else 5
@property
def weight(self):
return self._weight
@weight.setter
def weight(self, weight):
self._weight = weight if 0 < weight else 5
def area(self):
print('矩形的面积为%.2f' % (self._lang * self._weight))
def perimeter(self):
print('矩形的周长为%.2f' % ((self._lang + self._weight) * 2))
def main():
rec1 = Rectangle(7, 55)
rec1.weight = 6
rec1.area()
rec1.perimeter()
if __name__ == '__main__':
main()
静态方法
我们在类中定义的方法并不是所有方法都是给对象传递消息并且让对象执行相应的动作的,有一些内置的方法在创建对象实例的时候就要执行,例如我们在定义一个三角形类的时候,我们可以给三角形类添加一个判断输入的三条边是否可以构成一个三角形的静态方法,这样我们在创建一个三角形之前就可以先进行判断输入的三条边能否构成一个三角形:
from math import sqrt
class Triangle(object):
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):
half = self.perimeter() / 2
return sqrt(half * (half - self._a) *
(half - self._b) * (half - self._c))
def main():
a, b, c = 3, 4, 5
# 静态方法和类方法都是通过给类发消息来调用的
if Triangle.is_valid(a, b, c):
t = Triangle(a, b, c)
print(t.perimeter())
# 也可以通过给类发消息来调用对象方法但是要传入接收消息的对象作为参数
# print(Triangle.perimeter(t))
print(t.area())
# print(Triangle.area(t))
else:
print('无法构成三角形.')
if __name__ == '__main__':
main()
继承和多态
说到继承就必须知道类和类之间的关系,类和类之间的关系大概分为三种:
线段上有两个点 - has-a - 关联
人使用了房子 - use-a - 依赖
学生是一个人 - is-a - 继承
继承就是在已有类的基础上创建新类,这其中的一种做法就是让一个类从另一个类那里将属性和方法直接继承下来,从而减少重复代码的编写。提供继承信息的我们称之为父类,也叫超类或基类;得到继承信息的我们称之为子类,也叫派生类或衍生类。子类还可以定义自己的方法,因此子类的的能力是大于等于父类的,所以在实际开发中我们随时可以用子类去替换父类,我们称之为里氏替换原则。
# 定义一个父类人
class Person(object):
def __init__(self, name, age):
self._name = name
self._age = age
@property
def name(self):
return self._name
@name.setter
def name(self, name):
self._name = name
@property
def age(self):
return self._age
@age.setter
def age(self, age):
self._age = age if 25 <= age < 70 else 40
def watch(self):
print('%s正在看AV' % self._name)
# 定义一个子类老师继承人这个类
class Teacher(Person):
def __init__(self, title, name, age):
super().__init__(name, age) # 调用父类里的构造方法
self._title = title
def teach(self):
print('%s老师正在教%s' % (self._name, self._title))
def __str__(self):
return '%s老师' % self._name + '年龄:%s' % self._age
class Student(Person):
def __init__(self, name, age, grade):
super().__init__(name, age)
self._grade = grade
def study(self):
print('%s年级的%s正在学习' % (self._grade, self._name))
# 方法重写(over ride)
# 子类在继承父类的方法之后,对方法进行了重新实现
# 当我们给子类发送watch消息时执行的是子类重写过的方法
def watch(self):
print('学生正在学习技术')
def main():
teacher = Teacher('python', '骆昊', '18')
teacher.teach()
teacher.watch() # 调用父类的方法
if __name__ == '__main__':
main()
在上面的例子中我们可以看到学生类重写了watch方法,事实上如果子类足够,我们可以大量的重写父类的同一个方法,那么父类的这个方法在子类中就就有不同的实现版本,我们在不同的子类中调用这个方法的时候,做出的行为也是不一样的,我们将这个称之为多态
from abc import ABCMeta, abstractmethod
class Person(object, metaclass=ABCMeta):
def __init__(self, name, age):
self.name = name
self.age = age
# 构造一个走路方法
@abstractmethod
def walk(self):
pass
class Human(Person):
def __init__(self, name, age):
# 调用父类构造方法
super().__init__(name, age)
def walk(self):
# 重写父类的walk方法
print('%s正在以10km\h的速度走路' % self.name)
class Runner(Person):
def __init__(self, name, age):
# 调用父类构造方法
super().__init__(name, age)
def walk(self):
print('%s正在以20km\h的速度走路' % self.name)
def main():
human = Human('阿萨德', 18)
runner = Runner('周星驰', 30)
human.walk()
runner.walk()
if __name__ == '__main__':
main()
这里我们导入abc模块的目的是将Person类构造成一个抽象类,Python语法中并没有构建抽象类的方法,所以我们需要通过abc模块中的ABCMeta元类和abstractmethod包装器来打造出抽象类的效果,抽象类是不能够创建对象的,他存在的意义就是让其他类去继承他,而抽象类中的walk方法使用abstractmethod包装器来构成抽象方法,在子类中必须重写这个方法,否则程序就会报错,而不同的子类重写出了不同的walk方法,在调用的时候出现了不同的行为,这就是多态行为。
一些静态方法和继承多态的实例:
# 定义一个类描述分数
# 分子分母
# 加减乘除约分
class MyFraction(object):
def __init__(self, numerator, denominator):
if denominator == 0:
raise ValueError('分母不能为0')
self._numerator = numerator
self._denominator = denominator
@property
def numerator(self):
return self._numerator
@numerator.setter
def numerator(self, numerator):
self._numerator = numerator
@property
def denominator(self):
return self._denominator
@denominator.setter
def denominator(self, denominator):
self._denominator = denominator if denominator != 0 else self._denominator
# 静态方法,给类使用而不给对象使用的方法
@staticmethod
def mcm(a, b):
"""
计算a,b的最小公倍数
:param a: 第一个数
:param b: 第二个数
:return: 返回a,b的最小公倍数
"""
temp = min(a, b)
while temp % a != 0 or temp % b != 0:
temp += min(a, b)
return temp
def add(self, other):
temp = MyFraction.mcm(self._denominator, other.denominator)
x = temp / self._denominator
y = temp / other.denominator
return MyFraction(self._numerator * x + other.numerator * y, temp).simple()
def sub(self, other):
temp = MyFraction.mcm(self._denominator, other.denominator)
x = temp / self._denominator
y = temp / other.denominator
return MyFraction(self._numerator * x - other.numerator * y, temp).simple()
def mul(self, other):
return MyFraction(self._numerator * other.numerator, self._denominator * other.denominator).simple()
def div(self, other):
return MyFraction(self._numerator * other.denominator, self._denominator * other.numerator).simple()
def __add__(self, other):
return self.add(other)
def __sub__(self, other):
return self.sub(other)
def __mul__(self, other):
return self.mul(other)
def __truediv__(self, other):
return self.div(other)
def simple(self):
temp1 = self._numerator
temp2 = self._denominator
c = temp1 % temp2
while c != 0:
temp1 = temp2
temp2 = c
c = temp1 % temp2
self._numerator /= temp2
self._denominator /= temp2
return MyFraction(self._numerator, self._denominator)
def __str__(self):
if self._denominator == 1:
return '%d' % self._numerator
elif self._numerator == 0:
return 0
return '%d/%d' % (self._numerator, self._denominator)
def main():
f1 = MyFraction(1, 2)
f2 = MyFraction(5, 14)
f3 = MyFraction(5, 9)
print(f1 + f2 + f3)
print(f1 - f2)
print(f1 * f2)
print(f1 / f2)
if __name__ == '__main__':
main()
# ♠♥♣♦
# blackjack
from random import randrange
class Card(object):
"""一张牌"""
def __init__(self, suite, face):
self._suite = suite
self._face = face
@property
def face(self):
return self._face
@face.setter
def face(self, face):
self._face = face if 0 < face <= 13 else self._face
@property
def suite(self):
return self._suite
def __str__(self):
if self._face == 1:
face_str = 'A'
elif self._face == 11:
face_str = 'J'
elif self._face == 12:
face_str = 'Q'
elif self._face == 13:
face_str = 'K'
else:
face_str = str(self._face)
return '%s%s' % (self._suite, face_str)
class Poker(object):
"""一副牌"""
def __init__(self):
self._cards = []
for suite in '♠♥♣♦':
for face in range(13):
card = Card(suite, face + 1)
self._cards.append(card)
self._current = 0
@property
def cards(self):
return self._cards
def shuffle(self):
"""洗牌(随机乱序)"""
self._current = 0
for index in range(len(self._cards)):
pos = randrange(len(self._cards) - 1)
self._cards[index], self._cards[pos] = self._cards[pos], self._cards[index]
def deal(self):
"""发牌"""
card = self._cards[self._current]
self._current += 1
return card
@property
def has_more(self):
"""有牌返回True没牌返回False"""
return self._current < len(self._cards)
class Player(object):
def __init__(self, name):
self._name = name
self._cards = []
@property
def cards(self):
return self._cards
@property
def name(self):
return self._name
def get(self, card):
self._cards.append(card)
def arrange(self):
self._cards.sort(key=get_key)
def get_key(card):
return card.face
def show(player):
print('%s玩家的牌为:' % player.name, end='')
for card in player.cards:
print(card, end=' ')
print()
def get_cards(player, p1):
"""抽牌"""
player.get(p1.deal())
player.arrange()
def sum_card(card, x):
"""计算所有牌的总和"""
if card.face == 11 or card.face == 12 or card.face == 13:
x += 10
elif card.face == 1:
x += 11
else:
x += card.face
return x
def main():
p1 = Poker()
p1.shuffle()
players = [] # 用于存放player对象
score = [] # 用于存放player对象的点数
n = int(input('请输入玩家人数(2 - 6):'))
# 将玩家对象append到players列表中
for x in range(n):
name = input('请输入玩家%s的名字:' % str(x + 1))
players.append(Player(name))
# 先给每个玩家发2张牌
for _ in range(2):
for player in players:
get_cards(player, p1)
for i, player in enumerate(players):
print('-----------第%d位玩家-----------' % (i + 1))
my_sum = 0 # 用于临时存储玩家点数
# 先计算玩家初始点数
for card in player.cards:
my_sum = sum_card(card, my_sum)
show(player)
print('%s玩家的点数为%d' % (player.name, my_sum))
# 定义一个布尔值使玩家进入选择是否还需要一张牌
get_card = True
while get_card:
num = int(input('请问是否需要一张牌(1:需要, 2:不需要):'))
if num == 1:
my_sum = 0
a = 0 # 用于统计A这张牌多少次被当做11
get_cards(player, p1)
show(player)
for card in player.cards:
if card.face == 1:
a += 1
my_sum = sum_card(card, my_sum)
# 当a>0证明有A被当做11使用,这时候如果玩家点数超过21点就将A当做1使用
while a > 0:
if my_sum > 21:
my_sum -= 10
a -= 1
else:
break
# 如果最后统计出来的点数超过了21点就将点数赋为0传入到保存玩家点数的列表中
if my_sum > 21:
print('%s玩家的点数为%d' % (player.name, my_sum))
print('玩家%s点数超过21点出局' % player.name)
my_sum = 0
score.append(my_sum)
get_card = False
else:
print('%s玩家的点数为%d' % (player.name, my_sum))
# 如果玩家选择不再要牌就将玩家当前的点数存入到保存玩家点数的列表中
else:
print('%s玩家的点数为%d' % (player.name, my_sum))
score.append(my_sum)
get_card = False
print('-----------最终统计-----------')
for i, player in enumerate(players):
print('%s玩家的点数为%d' % (player.name, score[i]))
m = 0
# 找出玩家中点数最大的值
for x in range(len(score)):
m = score[x] if score[x] >= m else m
test = 0
# 判断是否有多个玩家点数都一样大且都是最大的
for x in range(len(score)):
if m == score[x]:
test += 1
print('-----------最终结果-----------')
# 如果只有一个玩家的点数是最大的就输出这个玩家胜利
if test == 1:
for x in range(len(score)):
if m == score[x]:
print('%s玩家取得了最后的胜利' % players[x].name)
# 如果不只一个玩家点数是最大的就输出多个玩家共同胜利
else:
for x in range(len(score)):
if m == score[x]:
print(players[x].name, end=' ')
print('共同取得了最后的胜利')
if __name__ == '__main__':
main()
# Python没有从语言层面支持抽象类的概念
# 我们可以通过abc模块来制造抽象类的效果
# 在定义类的时候通过制定metaclass = ABCMeta可以将类声明为抽象类
# 抽象类是不能创建对象的,抽象类存在的意义是专门们拿给其他类继承的
# abc模块中还有一个包装器吼叫abstractmethod
# 通过这个包装器可以将方法被包装为抽象方法,必须要求子类进行重写
from abc import ABCMeta, abstractmethod # abstract class
class Employee(object, metaclass=ABCMeta):
def __init__(self, name):
self._name = name
@property
def name(self):
return self._name
@abstractmethod
def get_salary(self):
pass
class Manager(Employee):
def get_salary(self):
return 15000
class Programer(Employee):
def __init__(self, name):
super().__init__(name)
self._work_hour = 0
@property
def work_hour(self):
return self._work_hour
@work_hour.setter
def work_hour(self, work_hour):
self._work_hour = work_hour if 0 < work_hour < 24 * 30 else 0
def get_salary(self):
return 150.0 * self._work_hour
class SalesMan(Employee):
def __init__(self, name):
super().__init__(name)
self._sales = 0
@property
def sales(self):
return self._sales
@sales.setter
def sales(self, sales):
self._sales = sales if sales >= 0 else 0
def get_salary(self):
return 1200.0 + self._sales * 0.05
def main():
emps = [
Manager('刘备'), Programer('程序猿'), SalesMan('销售猿')
]
for emp in emps:
# isinstance识别对象的类别
if isinstance(emp, Programer):
emp.work_hour = int(input('请输入%s本月工作时间:' % emp.name))
elif isinstance(emp, SalesMan):
emp.sales = int(input('请输入%s本月销售额:' % emp.name))
# 同样是调用get_salary方法,但是不同的员工表现出了不同的行为
# 因为三个子类都重写了了get_salary方法,所以此处有多态行为
print('%s得到的工资是:%.2f元' % (emp.name, emp.get_salary()))
if __name__ == '__main__':
main()
pygame
主要是为了熟悉和运用面向对象,所以就贴一段代码
from random import randint
from math import sqrt
import pygame
class Ball(object):
def __init__(self, center, color, radius, sx, sy):
"""
小球的初始属性
:param center:小球的初始位置
:param color: 小球的颜色
:param radius: 小球的半径
:param sx: 小球的横向移动速率
:param sy: 小球的纵向移动速率
"""
self._center = center
self._color = color
self._radius = radius
self._sx = sx
self._sy = sy
def move(self):
"""小球的移动"""
x, y = self._center[0], self._center[1]
x += self._sx
y += self._sy
self._center = (x, y)
if x >= 1500 or x <= 0:
self._sx = -self._sx
if y >= 800 or y <= 0:
self._sy = -self._sy
def eat(self, other):
"""判断大球吃小球"""
x1, y1 = self._center[0], self._center[1]
x2, y2 = other._center[0], other._center[1]
s = sqrt((x2 - x1) ** 2 + (y1 - y2) ** 2)
if s < self._radius + other._radius:
# 如果A的半径大于B那么A的面积就变为AB之和B的半径变为0,反之同理
if self._radius > other._radius:
self._radius = int(sqrt(self._radius ** 2 + other._radius ** 2))
return True
return False
def draw(self, screen):
"""画小球"""
pygame.draw.circle(screen, self._color, self._center, self._radius, 0)
def main():
balls = []
pygame.init()
screen = pygame.display.set_mode([1500, 800])
pygame.display.set_caption('大球吃小球')
clock = pygame.time.Clock()
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.MOUSEBUTTONDOWN:
if event.button == 1:
center = event.pos
color = random_color()
radius = randint(1, 10) * 10
sx, sy = randint(-10, 10), randint(-10, 10)
ball = Ball(center, color, radius, sx, sy)
balls.append(ball)
clock.tick(60)
re_fresh(screen, balls)
for ball in balls:
ball.move()
for other in balls:
if ball.eat(other):
balls.remove(other)
pygame.quit()
def re_fresh(screen, balls):
bg_color = [205, 205, 205]
screen.fill(bg_color)
for ball in balls:
ball.draw(screen)
pygame.display.flip()
def random_color():
red = randint(0, 255)
blue = randint(0, 255)
green = randint(0, 255)
return red, blue, green
if __name__ == '__main__':
main()
数据接口
文件的读写与异常处理机制
必须先用Python内置的open()函数打开一个文件,创建一个file对象,相关的方法才可以调用它进行读写。
file object = open(file_name [, access_mode][, buffering])
file_name:文件名
access_mode:access_mode决定了打开文件的模式,默认文件访问模式为只读(r)
buffering:如果buffering的值被设为0,就不会有寄存。如果buffering的值取1,访问文件时会寄存行。如果将buffering的值设为大于1的整数,表明了这就是的寄存区的缓冲大小。如果取负值,寄存区的缓冲大小则为系统默认。
一些常用打开文件模式
模式 | 描述 |
---|---|
r | 以只读方式打开文件。文件的指针将会放在文件的开头。这是默认模式。 |
rb | 以二进制格式打开一个文件用于只读。文件指针将会放在文件的开头。这是默认模式。 |
w | 打开一个文件只用于写入。如果该文件已存在则将其覆盖。如果该文件不存在,创建新文件。 |
wb | 以二进制格式打开一个文件只用于写入。如果该文件已存在则将其覆盖。如果该文件不存在,创建新文件。 |
a | 打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。 |
ab | 以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。 |
如果执行了open()方法那么在执行完之后一定要执行一个close()方法来关闭打开的文件
def main():
# 打开一个文件
fo = open("foo.txt", "wb")
print("文件名: ", fo.name)
# 关闭打开的文件
fo.close()
if __name__ == '__main__':
main()
但是在进行文件处理的时候,有可能会遇到下面2个问题:
1:可能忘记关闭文件句柄;
2:文件读取数据发生异常,没有进行任何处理。
这个时候我们就可以使用try-except-finally语句和with语句来处理
def main():
# 打开一个文件
try:
with open("foo.txt", "w",encoding='utf-8') as fs:
fs.write('xxx')
except:
print('xxx')
if __name__ == '__main__':
main()
这样就可以很好的处理文件处理时发生的意外情况
json的基本使用
在我们进行文件读写的时候,如果我们想要将一个列表,字典之类的数据写入到一个文件中是非常麻烦的,因此为了方便我们对文件进行读写,这里引入了json(JavaScript Object Notation)格式,通过将数据转化为json格式再存储到文件中,大大降低了代码的复杂度,下面的代码提供了json导入和导出数据的方法。
# json: JavaScript Object Notation
# URL: Uniform Resource Locator
import json
def main():
my_list = [1, 3, [4, 5, 6]]
print(stu)
my_dict = {
'name': '陈昭旭',
'age': 17,
'friends': ['阿萨德', '主线程'],
'cars': [
{'brand': 'auto', 'max_speed': 120},
{'brand': 'bmw', 'max_speed': 180},
{'brand': 'benz', 'max_speed': 200}
]
}
try:
with open('data.json', 'w', encoding='utf-8') as fs:
# dump方法,写入文件
json.dump(my_dict, fs)
with open('data.json', 'r', encoding='utf-8') as fs:
# load方法导出文件
your_dict = json.load(fs)
print(your_dict)
except IOError as e:
print(e)
except FileNotFoundError as f:
print(f)
print('保存数据完成')
if __name__ == '__main__':
main()
requests模块
requests模块可以使python发送网络请求变得轻松很多,在我们从网络上收发数据的时候,很多时候都是json格式的数据,因此我们可以利用requests模块与json模块来对网络上的数据进行收发,与将网络上的数据存储到文件中
import json
import requests
def main():
# 发送不带参数的get请求
resp = requests.get('http://api.tianapi.com/mobile/?key=16c0d351f62f25bab4d349385ee6a9b9&num=10')
# 将返回的字符串存入my_dict(text返回的是字符串)
my_dict = json.loads(resp.text)
for temp_dict in my_dict['newslist']:
# 获取返回的url地址
pic_url = temp_dict['url']
# 获取返回的title
filename = temp_dict['title']
try:
with open(filename, 'w') as fs:
# 将返回的地址写入到文件里,文件名为返回的标题
fs.write(pic_url)
except IOError as e:
print(e)
if __name__ == '__main__':
main()