1.听写
借助封装和继承
Tiger(老虎)
属性:品种 年龄 性别
行为:吃饭
Cat(猫)
属性:昵称 品种 年龄 性别
行为:吃饭 爬树
创建对象:
东北虎 2 雌---调用吃饭的行为---打印东北虎吃肉
妮妮 加菲猫 1 雄---调用吃饭的行为---打印妮妮吃鱼
调用爬树的行为---打印妮妮在树上
class Animal:
def __init__(self, brand, age, sex):
self.__brand = brand
self.__age = age
self.__sex = sex
def eat(self):
print("吃饭")
'''
隐藏起来的内容只有在当前类中可以使用 出了当前类没有任何意义
想对外提供 只能提供获取或者设置的方法
'''
def get_brand(self):
return self.__brand
class Cat(Animal):
def __init__(self, brand, age, sex, name):
super().__init__(brand, age, sex)
self.__name = name
def eat(self):
print("%s在吃鱼" % self.__name)
def climb(self):
print("%s在树上" % self.__name)
class Tiger(Animal):
def __init__(self, brand, age, sex):
super().__init__(brand, age, sex)
def eat(self):
print("%s在吃肉" % self.get_brand())
from listen.cat_tiger import *
def main():
#创建一个老虎对象
tiger = Tiger("东北虎", 2, "雌")
tiger.eat()
#创建一个猫的对象
cat = Cat("加菲猫", 1, "雄", "妮妮")
cat.eat()
cat.climb()
if __name__ == "__main__":
main()
2.今日课堂概述
1.继承体系中__slots__的使用
2.多继承与多层继承中子类的继承顺序与如何调用多个父类中的构造方法
3.get/set方法属性化
4.运算符系统方法重写
5.类属性和类方法
6.动态语言与静态语言
7.深拷贝与浅拷贝
8.常用系统属性
__name__
__dict__
__bases__
3.继承体系中slots的使用
__slots__限制不让对象随意的动态添加属性--->[限制只会限制当前类]
如果在继承体系中:
父类中做了限制 在子类中没有设置slots的字段值 这种限制对应子类无效
想让子类也有限制作用 需要在子类中把slots这个字段声明 是在父类的基础上进行添加
class Animal:
__slots__ = ("brand", "age")
def __init__(self, brand, age):
self.brand = brand
self.age = age
class Dog(Animal):
__slots__ = ("name")
def __init__(self, name, brand, age):
self.name = name
super().__init__(brand, age)
self.sex = "雄" # 错误
# 调用打印方法时 打印对象的引用变量
def __str__(self):
return "Dog name:%s brand:%s age:%d" % (self.name, self.brand, self.age)
from slots_pack.animal import *
def main():
# 创建一个dog对象
dog = Dog("旺财", "二哈", 2)
print(dog)
if __name__ == "__main__":
main()
4.多继承与多层继承中子类的继承顺序与如何调用多个父类中的构造方法
多继承与多层继承中子类的继承顺序 ---> 采用广度继承制度
这种状态的查看时机是子类没有从父类中继承任何属性 但是确实是存在继承关系
可以从类名.mro() ---> 查看当前类的继承体系
类名.__bases__ ---> 查看的是直接父类
class Animal:
def __init__(self):
print("动物祖宗初始化方法")
class Mammal(Animal):
def __init__(self):
print("哺乳类")
super().__init__()
class Runnable(Animal):
def __init__(self):
print("爬行类")
super().__init__()
class Dog(Mammal, Runnable):
def __init__(self):
print("底层子类")
super().__init__()
res = Dog.mro()
print(res)
'''
[<class '__main__.Dog'>, <class '__main__.Mammal'>, <class '__main__.Runnable'>, <class '__main__.Animal'>, <class 'object'>]
底层子类
哺乳类
爬行类
动物祖宗初始化方法
'''
如何调用多个父类中的构造方法
如果多个父类中有属性需要子类继承 子类该如何调用父类的__init__方法
父类类名.__init__(self, 父类需要的属性列表)
class Father:
def __init__(self,name,sex):
print("父亲")
self.name = name
self.sex = sex
class Mother:
def __init__(self, love):
print("母亲")
self.love = love
class Son(Father, Mother):
def __init__(self, name, love, sex):
# 父类类名.__init__(self, 父类参数列表)
Father.__init__(self, name, sex)
Mother.__init__(self, love)
5.get/set方法属性化
封装之后 外界想获取属性值:
提供get方法 ---> 调用方法才可以获取属性值
封装之后 外界想设置属性值:
提供set方法 ---> 调用方法在方法中传递数据
属性化 —> 将调用方法的格式 转化为 类似于普通属性的获取方式
将get方法属性化的方式
在get方法上 添加装饰器
@property
调用方法时 可以只写方法名 不用添加小括号 自动将方法进行调用
值 = 对象.get方法名
注意:
如果没有添加@property装饰器 就是一个普通的方法 调用的时候必须添加小括号
将set方法属性化的方式
set属性化的装饰器 是在对应的get方法的基础上衍生来的
@get方法名.setter
属性化之后 调用就类似与普通属性那样使用等号来进行赋值
对象.set方法名 = 值
注意:
如果没有添加@get方法名.setter装饰器 就是一个普通的方法 调用的时候必须添加小括号 并在小括号中赋值
class Person:
__slots__ = ("name", "__age", "__sex")
def __init__(self, name, age, sex):
self.name = name
self.set_age = age
self.set_sex = sex
@property
def get_age(self):
return self.__age
@get_age.setter
def set_age(self, age):
if age < 0:
self.__age = 0
else:
self.__age = age
@property
def get_sex(self):
return self.__sex
@get_sex.setter
def set_sex(self, sex):
if sex in ("男", "女"):
self.__sex = sex
else:
self.__sex = "男"
def __str__(self):
return "Person name:%s age:%d sex:%s" % (self.name, self.__age, self.__sex)
__repr__ = __str__
from set_get_property.person import Person
def main():
# 创建人的对象
person = Person("杨阳", 18, "女")
print(person)
# 获取这个对象的名字
name = person.name
print(name)
# 将get方法进行属性化 调用方法的时候 不使用小括号来调用 直接把方法名当做普通的属性来进行使用
sex = person.get_sex
print(sex)
# 获取年龄
age = person.get_age
print(age)
# 赋值
# 给name进行重新赋值
person.name = "羊羊羊"
print(person)
# 将set方法属性化之后 调用的时候直接调用方法名即可 赋值的时候 用类似于普通属性那种等号赋值方式赋值即可
person.set_sex = "男"
print(person)
# 修改年龄
person.set_age = 20
print(person)
if __name__ == "__main__":
main()
6.运算符系统方法重写
字符串:
+
*
>=
<=
>
<
==
!=
%
class Pointer:
def __init__(self, pass_x, pass_y):
self.x = pass_x
self.y = pass_y
def __add__(self, other):
# self + other
new_x = self.x + other.x
new_y = self.y + other.y
new_p = Pointer(new_x, new_y)
return new_p
def __mul__(self, value):
# self * value
self.x = self.x * value
self.y *= 5
return self
def __eq__(self, other):
#self == other
if self.x == other.x and self.y == other.y:
return True
else:
return False
def __ne__(self, other):
#self != other
if self.x != other.x or self.y != other.y:
return True
else:
return False
def __ge__(self, other):
#self >= other
if self.x >= other.x or self.y >= other.y:
return True
else:
return False
def __le__(self, other):
#self <= other
if self.x <= other.x or self.y <= other.y:
return True
else:
return False
def __gt__(self, other):
# self > other
if self.x > other.x or self.y > other.y:
return True
else:
return False
def __lt__(self, other):
#self < other
if self.x < other.x or self.y < other.y:
return True
else:
return False
def __mod__(self, value):
#self % value
self.x %= value
self.y %= value
return self
def __str__(self):
return "Pointer x:%d y:%d" % (self.x, self.y)
__repr__ = __str__
p1 = Pointer(1, 2)
print(id(p1))
print(p1)
p2 = Pointer(1, 2)
print(id(p2))
# 将两个点的坐标相加 生成一个新的点的对象
res = p1 + p2
print(res)
print(id(res))
# 乘法 可以乘以一个数值n 将原有的对象的坐标点的值 坐标值是原有基础上数值n的倍数
res =p1 * 5
print(id(res))
print(res)
# 如果不重写 对应的方法 默认比较是地址
print(p1 == p2)
print(p1 == res)
# 进行重写 比较坐标点 一样返回True
p3 = Pointer(2, 4)
p4 = Pointer(2, 4)
print(p3 == p4)
使用比较多的
__eq__(self, other):
如果不重写 默认比较的是地址
重写的话一般是判定伪相等 只要两个对象满足某些需求 就判定两个对象相等
例如: 在程序中有一个人类
属性: 身份证号
现在有两个人类对象 以现实生活需求判定 这两个对象是否是同一个人
只要两个对象的身份证号一致 视为同一个人
class Person:
def __init__(self, cardid, name):
self.name = name
self.cardid = cardid
def __eq__(self, other):
#self == other
if self.cardid == other.cardid:
return True
else:
return False
p1 = Person(100001, "张三")
p2 = Person(100001, "李四")
p3 = Person(100002, "张三")
print(p1 == p2) # True
print(p1 == p3) # False
7.类属性和类方法:
对象属性
在创建对象的时候 self后面的变量名就是对象属性
被包含在__init__方法中的
类属性
直接被包含在类体中 与其他方法是平级的
什么时候使用类属性?
当该类所有对象的某个属性的值一致时 就可以将其提为类属性 类属性是被该类所有对象所共享的
类属性是属于类的 被加载到方法去中 只要是该类对象 就具有了该属性
使用的时候 可以使用对象进行调用 也可以使用类进行调用 建议使用类进行调用
案例:
人类:
name
age
sex
country[国籍] ---> 所有人的国籍都是CH
class Person:
__slots__ = ("name", "age", "sex")
__country = "CH"
def __init__(self, name, age, sex):
self.name = name
self.age = age
self.sex = sex
@classmethod
def get_country(cls):
print(cls)
return cls.__country
def __str__(self):
return "Person name:%s age:%d sex:%s country:%s" % (self.name, self.age, self.sex, self.__country)
__repr__ = __str__
from class_property_method.person import Person
def main():
p1 = Person("张三", 20, "男")
p2 = Person("李四", 21, "男")
p3 = Person("王五", 22, "女")
print(p1)
print(p2)
print(p3)
print(Person.get_country())
print(p1.get_country())
if __name__ == "__main__":
main()
'''
Person name:张三 age:20 sex:男 country:CH
Person name:李四 age:21 sex:男 country:CH
Person name:王五 age:22 sex:女 country:CH
<class 'class_property_method.person.Person'>
CH
<class 'class_property_method.person.Person'>
CH
'''
对象方法:
特点: 方法的第一个形参为self ---> 表示的是调用方法的对象
调用该方法通过对象来调用
类方法:
需要装饰器 @classmethod 将方法装饰成类方法
类方法的第一个形参: cls ---> 表示的是当前类
这个参数与self类似 不需要手动传值 通过类或者对象调用时 会自动将其类型赋值给cls
什么时候使用类方法?
类方法一般是操作类属性的
类属性和类方法建议使用类来进行调用
静态方法[了解]
需要使用装饰器 @staticmethod 将方法装饰成静态方法
静态方法中的形参个数是根据需求来确定的 没有多余的类似于对象方法的self 和 类方法的cls
一般来使用的话 也是通过类名来进行调用该
练习:
学生类:
name
age
sex
count ---> 学生个数
要求:
创建n个学生对象 统计学生个数
不管用哪个学生对象调用个数 个数都是一致的
例如:
s1 =
s2 =
s3 =
print(s1.count) # 3
如何检测对象被创建了 ---> init
class Student:
__stu_count = 0
def __init__(self, name, age, sex):
self.name = name
self.age = age
self.sex = sex
# 这个方法可以检测对象是否创建
Student.__stu_count += 1
@classmethod
def get_count(cls):
#print(self.name)
return cls.__stu_count
@staticmethod
def show_info(info):
print("这是一个静态方法")
if __name__ == "__main__":
s1 = Student("小明", 17, "男")
s2 = Student("小刚", 16, "男")
s3 = Student("小强", 18, "男")
print(s1.get_count()) # 3
print(s2.get_count()) # 3
print(s3.get_count()) # 3
Student.show_info("内容") # 这是一个静态方法
res = Student.get_count() # 3
print(res)
注意:
在类方法中不允许出现self
原因:
类方法加载的时候还没有对象 就不能使用与对象相关的内容
从创建对象的过程:
p = Person("小明", 12, "男")
内存中的变化:
1. 将Person类加载到方法区内存中
跟随者一起加载到内存中的有 类属性和类方法
2.在对中开辟一块内存 存放该类声明的对象 并产生一个地址
跟随者加载的有对象属性
3. 将创建对象是传递的数据 给对象属性进行初始化
4. 将对象的地址 赋予给变量p
8.静态语言和动态语言
静态语言:
声明变量时 必须先声明出该变量的类型 给变量赋值的时候 只能赋予该类型的数据
java
动态语言:
变量的类型是根据赋值来决定的
Python属于动态语言 --- 本身就是多态的
变量赋予什么类型的数据 展现就是对应的类型
class Pet:
def __init__(self, name, age, sex, power):
self.name = name
self.age = age
self.sex = sex
self.power = power
class PetDog(Pet):
def __init__(self, name, age, sex, power):
super().__init__(name,age,sex,power)
def look_home(self):
print(self.name, "在看家")
class PetCat(Pet):
def __init__(self, name, age, sex, power):
super().__init__(name, age, sex, power)
def catch_mouse(self):
print(self.name, "抓老鼠")
class Host:
def __init__(self, name):
self.name = name
# 根据不同的场景 随时切换不同的类型 这个就是多态
def introduce(self, pet):
# 如何判断变量接受的是什么类型
if isinstance(pet, PetDog):
print("我家狗叫%s, %d岁了, 会%s" % (pet.name, pet.age, pet.power))
elif isinstance(pet, PetCat):
print("我家猫叫%s, %d岁了, 会%s" % (pet.name, pet.age, pet.power))
from polymorphic.pet import *
def main():
dog = PetDog("旺财", 2, "雌", "两条腿走路")
cat = PetCat("加菲", 2, "雄", "装死")
host = Host("baby")
host.introduce(dog)
host.introduce(cat)
if __name__ == "__main__":
main()
9.常用字段
常用系统属性
__name__
--- 类来调用 结果类的字符串名字
__dict__
--- 一般常用与使用对象来调用
返回的是一个字典 字典中包含的数据 { 对象属性:属性值, 对象属性:属性值}
__bases__
--- 元组 包含继承的父类
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
if __name__ == "__main__":
res = Person.__name__
print(type(res)) # <class 'str'>
print(res) # Person
p = Person("小刚", 20)
res = p.__dict__
print(res) # {'name': '小刚', 'age': 20}
res = Person.__bases__
print(res) # (<class 'object'>,)
10.深拷贝和浅拷贝
对于不可变对象 是没有拷贝这个功能的
浅拷贝:
只会拷贝最外层对象 内层对象还是引用的原来的地址
深拷贝:
内外都会生成一个新的对象 占用的地址不一样
需要借助模块 copy
import copy
# 不可变对象是没有拷贝行为的 不管如何进行操作 获取的都是原来地 地址 没有生成新的对象
str0 = "abc"
print(id(str0)) # 2445453913424
# 对其进行浅拷贝
res = copy.copy(str0)
print(id(res)) # 2445453913424
# 对其进行深拷贝
res = copy.deepcopy(str0)
print(id(res)) # 2445453913424
class Person:
def __init__(self, name):
self.name = name
def __str__(self):
return "Person name:%s" % self.name
__repr__ = __str__
# 可变对象的拷贝行为
person = Person("小黄")
print("人类对象的地址:",id(person)) # 人类对象的地址: 2445454886728
list0 = ["abc", True, 10, person]
print("原列表中引用的人类对象的地址",id(list0[3])) # 2445454886728
print("原列表地址:", id(list0)) # 2445456412488
# 浅拷贝 将最外层对象拷贝出来一个新的对象 但是内部的其他对象引用还是原来的地址
new_list = copy.copy(list0)
print(new_list)
print("浅拷贝出来的新列表的地址:", id(new_list)) # 2445456411080
print("浅拷贝出来的新列表引用的人类对象的地址:", id(new_list[3])) # 2445454886728
# 深拷贝: 内外对象都会拷贝出来一个新的对象
deep_list = copy.deepcopy(list0)
print(deep_list)
print("深拷贝出来的新列表的地址:", id(deep_list)) # 2445456412496
print("深拷贝出来的新列表引用的人类对象的地址:", id(deep_list[3])) # 2445454886896
'''
列表的方法中 提供了浅拷贝的方法
凡是可变对象的 都提供了一个浅拷贝的方法
'''