前言回顾
1.私有化: 命名的时候两个下划线开头
只能是__age,而不是__age__(后者依旧能访问)
2.getter和setter
getter - 想要[获取]属性之前做点别的事情
setter - 想要给属性[赋值]的时候做别的事情
- 属性名前加一个'_'
- 添加getter
@property
def getter名(self):
做别的事情
return 值
- 添加setter
@getter名.setter
def setter名(self, 参数):
做别的事情
给属性赋值
3.方法
对象方法
怎么声明:直接声明在类中的函数
特点:有默认参数self,不需要传参,指向当前对象
怎么调用:对象调用
什么时候:实现函数的功能[需要]对象的属性类方法
怎么声明:声明函数前加@classmethod
特点:有默认函数cls,不需要传参,指向当前类
怎么调用:类调用
什么时候:实现函数的功能[不需要]对象的属性的前提下需要类的字段静态方法
怎么声明:声明函数前加@staticmethod
特点:没有默认参数(和普通函数一样)
怎么调用:类调用
什么时候:实现函数的功能既不需要对象属性也不需要类的字段
4.继承
python支持继承语法,默认的类都是继承object; 并且支持多继承
继承就是让子类直接拥有
子类添加内容
字段和方法直接声明
添加对象属性重写方法
注意:多继承的时候可以继承多个类的方法和字段,但是只能继承第一个父类的属性
(故一般不采用多继承)
class Person:
def __init__(self, name, age=0):
self.name = name
self.age = age
self.gender = '女'
def show(self):
print('姓名:%s 年龄:%s' % (self.name, self.age))
class Student(Person):
def __init__(self, name1, score, age=0):
super().__init__(name1, age)
self.stu_id = '001'
self.score = score
def show(self):
print('姓名:%s 年龄:%s 分数:%d' % (self.name, self.age, self.score))
stu = Student('小明', 90, 20)
print(stu.__dict__)
stu.show()
多继承(重写方法)
class Animal(object):
def __init__(self):
self.age = 0
self.weight = 10
@staticmethod
def eat(food):
print('动物吃%s' % food)
class Fly(object):
def __init__(self):
self.height = 100
self.speed = 80
def stop(self):
print('停止飞行')
# print(self.speed)
# print(self.height)
# self.speed = 0
# self.height = 0
class Bird(Animal, Fly):
pass
b1 = Bird()
Bird.eat('虫子')
b1.stop()
# print(b1.weight, b1.age)
# print(b1.height, b1.speed)
作业
from math import pi
1.建立一个汽车类Auto,包括轮胎个数,汽车颜色,车身重量,速度等属性,
并通过不同的构造方法创建实例。至少要求汽车能够加速 减速 停车。
再定义一个小汽车类CarAuto继承Auto并添加空调、CD属性,
并且重新实现方法覆盖加速、减速的方法
class Auto:
def __init__(self, color='白色', weight=50, speed=0):
self.tyre_count = 4
self.color = color
self.weight = weight
self.speed = speed
self.__max_speed = 300
def speed_up(self, value):
"""加速"""
new_speed =self.speed + value
if new_speed > self.__max_speed:
self.speed = self.__max_speed
else:
self.speed = new_speed
def speed_down(self, value):
"""减速"""
new_speed = self.speed - value
if new_speed < 0:
self.speed = 0
else:
self.speed = new_speed
def stop(self):
"""停车"""
self.speed = 0
class CarAuto(Auto):
def __init__(self, color='白色', weight=100, speed=0):
super().__init__(color, weight, speed)
self.air_conditioner = '格力'
self.CD = ''
c1 = CarAuto('红色')
print(c1.__dict__)
2.创建一个Person类,添加一个类字段用来统计Person类的对象的个数
# 创建对象一定会调用__init__方法,故统计方法的使用次数即可
class Person:
count = 0
def __init__(self):
Person.count += 1
# for i in range(10):
# Person()
# print(Person.count)
3.创建一个动物类,拥有属性:性别、年龄、颜色、类型 ,要求打印这个类的对象的时候以'/XXX的对象: 性别-? 年龄-? 颜色-? 类型-?/'的形式来打印
class Animal:
"""动物类"""
def __init__(self, gender='公', age=1, color='黑色', type1='爬行'):
self.gender = gender
self.age = age
self.color = color
self.type = type1
def __repr__(self):
return '/%s的对象: 性别-%s 年龄-%d 颜色-%s 类型-%s/' % (self.__class__.__name__, self.gender, self.age, self.color, self.type)
an1 = Animal('公', 3, '棕色', '哺乳')
print(an1)
4.写一个圆类, 拥有属性半径、面积和周长;要求获取面积和周长的时候的时候可以根据半径的值把对应的值取到。
但是给面积和周长赋值的时候,程序直接崩溃,并且提示改属性不能赋值
class ReadOnlyError(Exception):
def __str__(self):
return '属性不能赋值!'
class Circle:
"""圆类"""
def __init__(self, radius):
self.radius = radius
self._perimeter = 0
self._area = 0
# perimeter
@property
def perimeter(self):
return self.radius*pi*2
@perimeter.setter
def perimeter(self, value):
raise ReadOnlyError
# area
@property
def area(self):
return self.radius**2*pi
@area.setter
def area(self, value):
raise ReadOnlyError
歌词解析
5.(尝试)写一个类,其功能是:
1.解析指定的歌词文件的内容
2.按时间显示歌词
提示:歌词文件的内容一般是按下面的格式进行存储的。
歌词前面对应的是时间,在对应的时间点可以显示对应的歌词
# 直接赋值是不会经过getter和setter的,故不要用参数
class Lyric:
def __init__(self, word):
self._time = 0
self.word = word
@property
def time(self):
return self._time
@time.setter
def time(self, value):
# value = '[00:45.99'
fen = float(value[1:3])
miao = float(value[4:])
self._time = fen*60 + miao
def __repr__(self):
return str(self.__dict__)
class LyricAnalysis:
def __init__(self, name):
self._name = name
self.__all_lyric = []
@property
def name(self):
return self._name
@name.setter
def name(self, value):
self._name = value
self.__all_lyric.clear()
def __analysis_file(self):
if not self.__all_lyric:
print('=====解析歌词=====')
# 1.解析歌词文件中的内容
with open('files/%s.txt' % self.name, encoding='utf-8') as f:
while True:
# 读一行内容
line = f.readline()
# 切割字符串
lines = line.split(']')
# 遍历时间创建歌词对象
for time_str in lines[:-1]:
lyric_obj = Lyric(lines[-1])
lyric_obj.time = time_str
# print(lyric_obj.__dict__)
self.__all_lyric.append(lyric_obj)
if not line:
break
# 2.对当前歌的歌词进行时间排序
self.__all_lyric.sort(reverse=True, key=lambda item: item.time)
print(self.__all_lyric)
def get_word(self, time):
# 解析歌词文件
self.__analysis_file()
# 3.根据时间找对应的歌词
for item in self.__all_lyric:
if item.time < time:
return item.word
return '歌名'+self.name
l1 = LyricAnalysis('蓝莲花')
print(l1.get_word(30))
print(l1.get_word(40))
print(l1.get_word(10))
l1.name = '东风破'
print(l1.get_word(20))
print(l1.get_word(30))
print(l1.get_word(10))
print(l1.get_word(10))
运算符
python中所有的数据类型都是类,数据都是对象
所有的运算符对应的操作,本质都是在调用数据类型对应的魔法方法 (每个运算符都对应一个固定的魔法方法)
class Student:
def __init__(self, name, age=0, score=0):
self.name = name
self.age = age
self.score = score
def __repr__(self):
return str(self.__dict__)
# 重载加法运算符
# self + other = return 返回值 (other是任意数据,可以是对象,可以是int...)
def __add__(self, other):
return self.age + other.age
# 重载乘法运算符
def __mul__(self, other: int):
return self.score * other
# >
# def __gt__(self, other):
# return self.score > other.score
# <
def __lt__(self, other):
return self.score < other.score
# 注意:>和<只需要重载一个(因为另一个它可以取反)
stu1 = Student('小明', 18, 60)
stu2 = Student('小花', 22, 80)
print(stu1 + stu2)
print(stu1 * 10)
print(stu1 > stu2)
print(stu1 < stu2)
all_students = [stu1, stu2, Student('星星', 17, 55), Student('二狗', 18, 70)]
all_students.sort()
print(all_students)
练习:让Student的对象支持乘法运算,运算规则是:
<name:张三, age:10, score:0> * 3 = [<name:张三, age:10, score:0>, <name:张三, age:10, score:0>, <name:张三, age:10, score:0>]
import copy
class Dog:
def __init__(self, name, color='黄色'):
self.name = name
self.color = color
class Student:
def __init__(self, name, age=0, score=0):
self.name = name
self.age = age
self.score = score
def __repr__(self):
return '<'+str(self.__dict__)[1:-1]+'>'
# *
def __mul__(self, other):
# self = stu1, other = 2
result = []
for _ in range(other):
result.append(self)
return result
stu1 =Student('张三', 18, 90)
print(stu1)
result = stu1 * 2
print(result)
stu1.name = '小明'
print(result)
result[0].name = '小花'
print(stu1, result)
copy
深拷贝和浅拷贝
直接赋值
1.一个变量直接给另外一个变量赋值:直接将地址赋值,赋完后两个变量指向同一块内存区域,并且相互影响
stu2 = Student('Lisa', 18, 60)
stu3 = stu2
print(id(stu2), id(stu3))
stu2.age = 28
print(stu3)
2.浅拷贝和深拷贝(面试点!)
拷贝原理:将被拷贝的对象复制一份,产生一个新的数据,然后将新的数据的地址返回
a.浅拷贝
- 列表或字典的copy方法是浅拷贝、切片也是浅拷贝
- copy.copy(对象) - 复制指定的对象,产生一个新的对象 (不会复制子对象)
b.深拷贝
copy.deepcopy(对象) - 复制指定的对象,产生一个新的对象 如果这个对象中有其他的对象,子对象也会被复制
浅拷贝
dog1 = Dog('财财')
stu2 = Student('Lisa', 18, 60)
stu4 = copy.copy(stu2)
print(stu4)
stu2.name = '小花'
print(stu2)
print(stu4)
# 浅拷贝
# list1 = [1, 2, 3, 4]
# list2 = list1.copy()
# list1.append(6)
# print(list2)
深拷贝
stu2 = Student('Lisa', 18, 60)
stu4 = copy.deepcopy(stu2)
print(stu4)
stu2.name = '小花'
print(stu2)
print(stu4)
存储器
from sys import getrefcount
栈区间 -> 自动开辟自动释放
堆区间 -> 手动开辟手动释放
栈区间:变量 堆区间:对象(数据)
1.数据的存储(内存开辟)
python的变量都存储在栈区间,对象都在堆区间
声明变量或者给变量赋值,是先在内存(堆)中开辟空间存储数据,然后将数据地址保存在变量中
注意:但是数字和字符串特殊,如果是用数字或者字符串给变量赋值,不会直接开辟空间保存数据,
而是先在内存检测这个数据之前是否已经存储过,如果已经存储过直接用上次保存的数据,没有存储过的才会开辟新的空间保存数据
list1 = [1, 2]
list2 = [1, 2]
print(id(list1), id(list2))
# 上面list1和list2的内存地址不同
num1 = 10
num2 = 10
print(id(num1), id(num2))
# num1和num2的内存地址相同
2.内存的释放
引用计数
python每个对象都有一个属性叫引用计数,用来保存当前对象的引用的个数
python中的垃圾回收机制来判断一个对象是否销毁,就看这个对象的引用计数是否为零,如果为零就会被销毁
class Student:
def __init__(self):
self.name = '张三'
# Student() 没有引用计数,创建后就被销毁了
print('===引用计数===')
list1 = [1, 2]
print(getrefcount(list1)) # 结果为2,因为getrefcount(obj),obj为临时的引用,会增加个数
# def my_getrefcount(obj):
# return 获取obj对应的数据的引用个数
# my_getrefcount(list1)
# 让引用计数增加
list2 = list1
print(getrefcount(list1))
dict1 = {'a': list1}
print(getrefcount(list1))
num1 = 100
print(getrefcount(num1))
num2 = 100
print(getrefcount(num1))
# 让引用计数减少
print(getrefcount(list1))
list2 = 100
print(getrefcount(list1))
del dict1['a']
print(getrefcount(list1))
del list1
# print(getrefcount(list1))
套接字
server
# from socket import socket,AF_INET
from socket import *
进行通信的两端就是套接字;有两种类型,分别是服务器套接字、客户端套接字
1.创建套接字对象(买个电话机)
socket(family= , type= )
family - 设置ip协议类型,AF_INET(ipv4), AF_INET(ipv6)
type - 设置传输协议类型,SOCK_STREAM(TCP), SOCK_DGRAM(UDP)
server = socket(family=AF_INET, type=SOCK_STREAM)
2.绑定ip地址和端口(插电话线绑定电话号码)
bind((ip地址, 端口))
ip地址 - 字符串,服务器对应的ip地址
端口号 - int,端口号用来区分客户端(电脑、手机)上的不同的服务 0-65535,0-1024是著名端口(一般不选)
同一时间一个端口只能绑定一个服务
addr = ('127.0.0.1', 8080)
server.bind(addr)
3.开始监听请求(安电池,等别人打电话)
server.listen(512) # 监听个数
4.让服务器一直运行
while True:
print('监听状态...')
# 5.接收请求(接电话)
# 代码运行到这个位置,会停下来,等到有客户端给服务器发送请求为止...
connect, addr = server.accept()
print(addr, '连接成功')
while True:
# 6.接收数据(听别人说)
"""
recv(bufsize) - bufsize,设置一次性能够接收的数据大小的最大值
返回的数据类型是字节
"""
print('接收消息前')
re_data = connect.recv(1024)
print(re_data.decode(encoding='utf-8'))
print('接收消息后')
# 7.发送数据(说给别人听)
value = input('>>>')
connect.send(value.encode()) # 发送的数据必须是二进制
# 8.关闭连接(挂电话)
connect.close()
client
from socket import *
1.创建套接字对象
client = socket()
2.连接服务器
client.connect(('10.7.185.92', 8080))
while True:
# 3.发送消息
message = input('请输入:')
client.send(message.encode())
# 4.接收消息
re_data = client.recv(1024)
print(re_data.decode(encoding='utf-8'))