面向过程vs面向对象
面向过程编程:根据业务逻辑从上到下的写代码-----就是一个project写到底,重复利用性比较差
函数式:将某些特定功能代码封装到函数中------方便日后调用
面向对象:对函数进行分类封装,使开发更快捷更灵活
面向对象编程
面向对象三大特性
- 封装 根据 职责 将 属性 和 方法 封装 到一个抽象的 类 中
- 继承 实现代码的重用,相同的代码不需要重复的编写
- 多态 不同的对象调用相同的方法,产生不同的执行结果,增加代码的灵活度
Object 对象
一个对象即是一个类的实例化后实例,一个类必须经过实例化后方可在程序中调用,一个类可以实例化多个对象,每个对象亦可以有不同的属性.
Encapsulation 封装
在类中对数据的赋值、内部调用对外部用户是透明的,这使类变成了一个胶囊或容器,里面包含着类的数据和方法
Inheritance 继承
一个类可以派生出子类,在这个父类里定义的属性、方法自动被子类继承
Polymorphism 多态
多态是面向对象的重要特性,简单点说:“一个接口,多种实现”,指一个基类中派生出了不同的子类,且每个子类在继承了同样的方法名的同时又对父类的方法做了不同的实现,这就是同一种事物表现出的多种形态。
面向对象特征:
- 类(Class): 用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。对象是类的实例。一个类可以有多个对象;
- 类变量:类变量在整个实例化的对象中是公用的。类变量定义在类中且在函数体之外。类变量通常不作为实例变量使用。
- 数据成员:类变量或者实例变量用于处理类及其实例对象的相关的数据。
- 方法重写:如果从父类继承的方法不能满足子类的需求,可以对其进行改写,这个过程叫方法的覆盖(override),也称为方法的重写。
- 实例变量:定义在方法中的变量,只作用于当前实例的类。
- 继承:即一个派生类(derived class)继承基类(base class)的字段和方法。继承也允许把一个派生类的对象作为一个基类对象对待。例如,有这样一个设计:一个Dog类型的对象派生自Animal类,这是模拟"是一个(is-a)"关系(例图,Dog是一个Animal)。
- 实例化:创建一个类的实例,类的具体对象。
- 方法:类中定义的函数。
- 对象:创建出来的 对象 叫做 类 的 实例;对象包括两个数据成员(类变量和实例变量)和方法;
语法:
大驼峰命名法:类名写法:CapWords:
每一个单词的首字母大写、单词与单词之间没有下划线;
定义只包含方法的类
- 在
Python
中要定义一个只包含方法的类,语法格式如下:
class 类名:
def 方法1(self, 参数列表):
pass
def 方法2(self, 参数列表):
pass
- 方法 的定义格式和之前学习过的函数 几乎一样
- 区别在于第一个参数必须是
self
,
- 当一个类定义完成之后,要使用这个类来创建对象,语法格式如下:
对象变量 = 类名()
引用概念的强调
在面向对象开发中,引用的概念是同样适用的!
-
在
Python
中使用类 创建对象之后,tom
变量中 仍然记录的是 对象在内存中的地址 - 也就是
tom
变量 引用 了 新建的猫对象 - 使用
print
输出 对象变量,默认情况下,是能够输出这个变量 引用的对象 是 由哪一个类创建的对象,以及 在内存中的地址(十六进制表示)
提示:在计算机中,通常使用 十六进制 表示 内存地址
- 十进制 和 十六进制 都是用来表达数字的,只是表示的方式不一样
- 十进制 和 十六进制 的数字之间可以来回转换
%d
可以以 10 进制 输出数字%x
可以以 16 进制 输出数字
class Cat: """这是一个猫类""" def eat(self): print("小猫爱吃鱼") def drink(self): print("小猫在喝水") tom = Cat() tom.drink() tom.eat() print(tom)
类属性、类方法和实例属性、实例方法
Python
中 一切皆对象:
class AAA:
定义的类属于 类对象obj1 = AAA()
属于 实例对象因此,通常也会把:
- 创建出来的 对象 叫做 类 的 实例
- 创建对象的 动作 叫做 实例化
- 对象的属性 叫做 实例属性
- 对象调用的方法 叫做 实例方法
- 在程序运行时,类 同样 会被加载到内存
- 在
Python
中,类 是一个特殊的对象 —— 类对象 - 在程序运行时,类对象 在内存中 只有一份,使用 一个类可以创建出 很多个对象实例
- 除了封装 实例的属性和方法外,类对象有自己的属性 和 方法
类属性:类和实例都能调用类的属性;
类方法:类如果调用类方法使用装饰器@classmethod,类方法 类和实例都能调用;
实例属性:类不能调用实例属性;
实例方法:只能实例对象调用实例方法,类不能调用;
实例对象的属性和方法(有self参数)
class student(): #类属性 name = "小米" sex = 12 # 实例方法 def study(self): #self.name = "小明" # 类方法的self实例可以调用类属性 print("学生学习能力") # 实例方法 def eat(self): print("学生会吃饭") xiaoming = student() #实例对象 print(student.name) # 类能调用类属性 print(xiaoming.name) # 实例也能调用类属性 xiaoming.name = "小明" # 赋了一个实例属性, 类是拿不到实例属性 print(student.name) # 类是拿不到实例属性,还是小米 print(xiaoming.name) #小明 #student.name = "flowers" #赋一个类属性,类和实例都能拿到 # print(student.name) # print(xiaoming.name) xiaoming.study() #对象调用类方法 # print(student.study()) #报错:只能实例对象去调用 # print(xiaoming.study()) #输出学习能力和none,none因为没有返回值 # print(xiaoming.eat())
类属性
类属性 就是给 类对象 中定义的 属性,通常用来记录 与这个类相关 的特征,类属性 不会用于记录 具体对象的特征;
- 定义一个 工具类
- 每件工具都有自己的
name
- 需求 —— 知道使用这个类,创建了多少个工具对象?
class Tool(object): """类属性使用""" # 使用赋值语句定义类属性,记录所有工具对象的数量 count = 0 def __init__(self, name): self.name = name # 让类属性的值+1,创建实例时自动调用 Tool.count += 1 print(Tool.count) # 1. 创建工具对象 tool1 = Tool("斧头") tool2 = Tool("榔头") tool3 = Tool("水桶") # 2. 输出工具对象的总数 print("使用类调用类属性-->创建了%d个工具" %Tool.count) #类调用类属性:类名.类属性 结果3 print("使用实例调用类属性-->创建了%d个工具" %tool1.count) #实例调用类属性:对象.类属性(不推荐) 结果3 #注意:如果使用对象.类属性 = 值赋值语句,只会给对象添加一个属性,而不会影响到 类属性的值 tool1.count='铁锹' print(tool1.count) #实例属性是铁锹 print("工具对象总数 %d" % Tool.count) #工具总数3
tool1.count 对象名.属性:获取机制(首先在对象内查找属性,没有就往上找类属性)代码从上往下执行
装饰器
类方法 @classmethod
就是针对类对象定义的方法,在类方法内部可以直接访问类属性 或者调用其他的类方法,需要用 修饰器 @classmethod
来标识,告诉解释器这是一个类方法,类方法的 (第一个参数 cls),
由 哪一个类 调用的方法,方法内的 cls
就是 哪一个类的引用,和 实例方法 的第一个参数是 self
类似,提示 使用其他名称也可以,不过习惯使用 cls;
@classmethod
def 类方法名(cls):
pass
- 通过 类名. 调用 类方法,调用方法时,不需要传递
cls
参数 - 在方法内部
- 可以通过
cls.
访问类的属性 - 也可以通过
cls.
调用其他的类方法
- 可以通过
#在类封装一个 show_tool_count的类方法,输出使用当前这个类,创建的对象个数 class Tool(object): """类方法:@classmethod""" # 使用赋值语句定义类属性,记录所有工具对象的数量 count = 0 @classmethod def show_tool_count(cls): """显示工具对象的总数""" print("工具对象的总数 %d" % cls.count) #通过 cls.访问类的属性 def __init__(self, name): self.name = name # 让类属性的值+1,创建实例时自动调用 Tool.count += 1 # 1. 创建工具对象 tool1 = Tool("斧头") tool2 = Tool("榔头") tool3 = Tool("水桶") # 调用类方法 Tool.show_tool_count()
静态方法@staticmethod
既 不需要 访问 实例属性 或者调用 实例方法,也 不需要 访问 类属性 或者调用 类方法。这个时候,可以把这个方法封装成一个 静态方法,需要用修饰器 @staticmethod
来标识,告诉解释器这是一个静态方法;
通过 类名. 调用 静态方法
class Dog(object): """静态方法:@staticmethod""" # 狗对象计数 dog_count = 0 @staticmethod def run(): # 不需要访问实例属性也不需要访问类属性的方法 print("狗在跑...") def __init__(self, name): self.name = name
练习:
- 1) 查看帮助信息
- 2) 查看历史最高分
- 3) 创建游戏对象,开始游戏
class Game(object): # 游戏最高分,类属性 top_score = 0 @staticmethod def show_help(): print("帮助信息:让僵尸走进房间") @classmethod def show_top_score(cls): print("游戏最高分是 %d" % cls.top_score) def __init__(self, player_name): self.player_name = player_name def start_game(self): print("[%s] 开始游戏..." % self.player_name) # 使用类名.修改历史最高分 Game.top_score = 999 # 1. 查看游戏帮助 Game.show_help() # 2. 查看游戏最高分 Game.show_top_score() # 3. 创建游戏对象,开始游戏 game = Game("小明") game.start_game() # 4. 游戏结束,查看游戏最高分 Game.show_top_score()
self参数解释:
- 类的方法与普通的函数只有一个特别的区别——它们必须有一个额外的第一个参数名称, 按照惯例它的名称是 self;
- 在类封装的方法内部,
self
就表示 当前调用方法的对象自己 - 调用方法时,不需要传递
self
参数 - 在方法内部
- 可以通过
self.
访问对象的属性 - 也可以通过
self.
调用其他的对象方法 - 在 类的外部,通过
变量名.
访问对象的 属性和方法 - 在 类封装的方法中,通过
self.
访问对象的 属性和方法
class Cat(): """由哪一个对象调用的方法,方法内的 self就是哪一个对象的引用""" def eat(self): print(f"{self.name}小猫爱吃鱼") def drink(self): print(f"{self.name}小猫爱喝水") tom = Cat() tom.name = "Tom" #给对象外部设置属性,假如先调用方法 再设置属性就会报错,不推荐使用 tom.drink() print(tom) #<__main__.Cat object at 0x10eafd6a0>当前创建的猫对象在内存中的地址,以16进制表示 print(id(tom)) #以十进制表示引用地址 lazy_cat = Cat() lazy_cat.name = "懒猫" lazy_cat.eat() print(lazy_cat)
构造方法:__init__初始化方法:
类的实例化操作会自动调用 __init__() 方法;__init__()
是对象的内置方法;
当使用 类名()
创建对象时,会 自动 执行以下操作:
1)为对象在内存中 分配空间 —— 创建对象
2)为对象的属性设置初始值 —— 初始化方法(init
)__init__
方法专门定义一个类具有那些属性的方法,
初始化的同时设置初始值:
把希望设置的属性值,定义成 __init__
方法的参数
在方法内部使用 self.属性 = 形参
接收外部传递的参数
在创建对象时,使用 类名(属性1, 属性2...)
调用
#猫类 class Cat(): def __init__(self): """定义用 Cat 类创建的猫对象都有一个ame的属性""" self.name = "tom" def eat(self): print(f"{self.name}小猫爱吃鱼") def drink(self): print(f"{self.name}小猫爱喝水") tom = Cat() tom.drink() #人类 class Person(): """初始化方法""" def __init__(self,name,age,heigh): """ :param name:名字属性 :param age: 年龄属性 :param heigh: 身高属性 """ self.name = name self.age = age self.heigh = heigh def run(self): print(f"我叫{self.name},今年{self.age}岁,身高{self.heigh},每天早晨跑步...") def eat(self): print(f"我叫{self.name},今年{self.age}岁,身高{self.heigh},每天早晨吃早餐...") #创建小明和小美的对象并传入实参 xiaoming = Person("小明",14,170) xiaoming.run() xiaoming.eat() xiaomei = Person("小美",12,150) xiaomei.eat()
析构方法:__del__
方法:
析构函数: 在实例释放、销毁的时候自动执行的,通常用于做一些收尾工作, 如关闭一些数据库连接,关闭打开的临时文件;当一个 对象被从内存中销毁 前,会 自动 调用 __del__
方法,
生命周期:一个对象从调用 类名()
创建,生命周期开始, __del__
方法一旦被调用,生命周期结束,在对象的生命周期内,可以访问对象属性,或者让对象调用方法;
自定义对象输出内容: __str__
方法:
在 Python
中,使用 print
输出 对象变量,默认情况下,会输出这个变量 引用的对象 是 由哪一个类创建的对象,以及 在内存中的地址(十六进制表示)
如果在开发中,希望使用 print
输出 对象变量 时,能够打印 自定义的内容,就可以利用 __str__
这个内置方法了,注意:__str__
方法必须返回一个字符串
class Cat(): def __init__(self,new_name): self.name = new_name print("%s 猫的名字" %self.name) def run(self): print("%s猫咪会跑"% self.name) def __del__(self): print("%s 走了" %self.name) #希望使用 print输出对象变量时,能够打印自定义的内容,就可以利用 __str__ 这个内置方法 def __str__(self): return "我是小猫:%s" % self.name maomi = Cat("tom") maomi.run() print(maomi.name) print(maomi) #print输出对象__str__自定义的内容,如果不使用该方法,输出十六进制 # del 关键字可以删除一个对象 del maomi print("结束") #print(Cat.name) 注意:会报错,因为类不能访问类的形参
继承
定义:子类拥有父类的所有方法和属性
面向对象编程 (OOP) 语言的一个主要功能就是“继承”。继承是指这样一种能力:它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。
通过继承创建的新类称为“子类”或“派生类”。
被继承的类称为“基类”、“父类”或“超类”。
要实现继承,可以通过“继承”(Inheritance)和“组合”(Composition)来实现。
在某些 OOP 语言中,一个子类可以继承多个基类。但是一般情况下,一个子类只能有一个基类,要实现多重继承,可以通过多级继承来实现。
继承概念的实现方式主要有2类:实现继承、接口继承。
Ø 实现继承是指使用基类的属性和方法而无需额外编码的能力;
Ø 接口继承是指仅使用属性和方法的名称、但是子类必须提供实现的能力(子类重构爹类方法);
抽象类仅定义将由子类创建的一般属性和方法。
OO开发范式大致为:划分对象→抽象类→将类组织成为层次化结构(继承和合成) →用类与实例进行设计和实现几个阶段。
单继承和方法重写:
super()函数:新式类写法,函数是用于调用父类(超类)的一个方法,用来解决多重继承问题的
class Animal: """动物类""" def eat(self): print("吃---") def drink(self): print("喝---") def run(self): print("跑---") def sleep(self): print("睡---") class Dog(Animal): def bark(self): print("汪汪叫") class XiaoTianQuan(Dog): """3、方法重写以及扩展父类方法""" def fly(self): print("我会飞") #重写狗类方法, def bark(self): print("凶狠的狂叫...") # 使用 super(). 调用原本在父类中封装的方法 super().bark() # 增加其他子类的代码 print("哈哈,别叫了") #2、注意事项:继承的传递性,猫类方法不能被啸天犬调用 class Cat(Animal): """猫类继承动物类""" def catch(self): print("抓老鼠") # duoduo = Dog() # duoduo.eat() # duoduo.drink() # duoduo.run() # duoduo.bark() xtq = XiaoTianQuan() xtq.fly() xtq.bark() # 如果子类中,重写了父类的方法,在使用子类对象调用方法时,会调用子类中重写的方法 xtq.eat() #xtq.catch #注意:啸天犬继承了狗类,猫类虽然也继承了基类,但是不能调用
多继承
- 子类 可以拥有 多个父类,并且具有 所有父类 的 属性 和 方法
class A: def test(self): print("test 方法") class B: def demo(self): print("demo 方法") class C(A, B): """多继承可以让子类对象,同时具有多个父类的属性和方法""" pass # 创建子类对象 c = C() c.test() c.demo()
- 如果 不同的父类 中存在同名的方法,子类对象 在调用方法时,会调用根据顺序调用;
-
Python
中针对 类 提供了一个 内置属性__mro__
可以查看 方法 搜索顺序,从左至右 的顺序查找类;MRO 是method resolution order
,主要用于 在多继承时判断 方法、属性 的调用 路径;
class A: def test(self): print("A----test 方法") def demo(self): print("A----demo 方法") class B: def test(self): print("B----test 方法") def demo(self): print("B----demo 方法") class C(A, B): """多继承可以让子类对象,同时具有多个父类的属性和方法""" pass # 创建子类对象 c = C() c.test() c.demo() print(C.__mro__) #执行顺序:(<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>)
新式类与旧式(经典)类
object
是Python
为所有对象提供的 基类,提供有一些内置的属性和方法,可以使用dir
函数查看-
print(dir(类名)) ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'demo', 'test']
-
- 新式类:以
object
为基类的类,推荐使用 -
经典类:不以
object
为基类的类,不推荐使用 -
在
Python 3.x
中定义类时,如果没有指定父类,会 默认使用object
作为该类的 基类 ——Python 3.x
中定义的类都是 新式类 -
在
Python 2.x
中定义类时,如果没有指定父类,则不会以object
作为 基类,新式类 和 经典类 在多继承时 —— 会影响到方法的搜索顺序
class 类名(object): pass
继承练习:
import random as r #父类 class Fish: def __init__(self): self.x = r.randint(0,10) self.y = r.randint(0,10) def move(self): self.x -= 1 print("我的位置是:",self.x,self.y) #子类单继承 class Goldfish(Fish): pass class Ti(): def run(self): print("用于输出多重继承") #继承父类后进行方法重写 class Shark(Fish): def __init__(self): #Fish.__init__(self) #给定基类的名字才能重写 super().__init__() #super函数不用给定基类名字 self.hungry = True def eat(self): if self.hungry: print("吃货的梦想是天天吃O(∩_∩)O哈哈~") self.hungry=False else: print("太撑了,吃不下了!") #多重继承 class Dc(Fish,Ti): pass #实例化类 ff = Fish() ff.move() Goldfish = Goldfish() Goldfish.move() Shark = Shark() Shark.eat() Shark.move() dc = Dc() dc.run() dc.move()'''
继承练习:
class SchoolMember(object): members = 0 # 初始学校人数为0 def __init__(self, name, age): self.name = name self.age = age def tell(self): pass def enroll(self): '''注册''' SchoolMember.members += 1 print("\033[32;1mnew member [%s] is enrolled,now there are [%s] members.\033[0m " % ( self.name, SchoolMember.members)) def __del__(self): '''析构方法''' print("\033[31;1mmember [%s] is dead!\033[0m" % self.name) class Teacher(SchoolMember): def __init__(self, name, age, course, salary):
#SchoolMember.__init__(self,name,age) 经典类写法 # super() 新式类写法,函数是用于调用父类(超类)的一个方法,用来解决多重继承问题的. super(Teacher, self).__init__(name, age) self.course = course self.salary = salary self.enroll() def teaching(self): '''讲课方法''' print("Teacher [%s] is teaching [%s] for class [%s]" % (self.name, self.course, 's12')) def tell(self): '''自我介绍方法''' msg = '''Hi, my name is [%s], works for [%s] as a [%s] teacher !''' % (self.name, 'Oldboy', self.course) print(msg) class Student(SchoolMember): def __init__(self, name, age, grade, sid): super(Student, self).__init__(name, age) self.grade = grade self.sid = sid self.enroll() def tell(self): '''自我介绍方法''' msg = '''Hi, my name is [%s], I'm studying [%s] in [%s]!''' % (self.name, self.grade, 'Oldboy') print(msg) if __name__ == '__main__': t1 = Teacher("Alex", 22, 'Python', 20000) t2 = Teacher("TengLan", 29, 'Linux', 3000) s1 = Student("Qinghua", 24, "Python S12", 1483) s2 = Student("SanJiang", 26, "Python S12", 1484) t1.teaching() t2.teaching() t1.tell()
输出:
3.类变量和实例变量
实例变量优先于类变量
私有属性和私有方法
- 在实际开发中,对象 的 某些属性或方法 可能只希望 在对象的内部被使用,而 不希望在外部被访问到
- 私有属性 就是 对象 不希望公开的 属性
- 私有方法 就是 对象 不希望公开的 方法
定义方式:在 定义属性或方法时,在 属性名或者方法名前 增加 两个下划线,定义的就是 私有 属性或方法
Python
中,并没有 真正意义 的 私有
- 在给 属性、方法 命名时,实际是对 名称 做了一些特殊处理,使得外界无法访问到
- 处理方式:在 名称 前面加上 print(对象名.
_类名__属性/方法)
class Women(): """私有属性和私有方法""" def __init__(self,name): self.name = name self.__age = 18 def __secret(self): # 在对象的方法内部,是可以访问对象的私有属性的 print("%s 的年龄是 %d" % (self.name, self.__age)) xiaofang = Women("小芳") # 私有属性,在外界不能够被直接访问 # print(xiaofang.__age) # 私有方法,同样不允许在外界直接访问 # xiaofang.__secret() # 伪私有属性, # 通过_类名的方式外界可以访问到私有属性和方法 print(xiaofang._Women__age) # 伪私有方法, xiaofang._Women__secret() #练习2: class people: #定义基本属性 name = '' age = 0 #定义私有属性,私有属性在类外部无法直接进行访问 __weight = 0 #定义构造方法 def __init__(self,n,a,w): self.name = n self.age = a self.__weight = w def getName(self): return self.__weight def speak(self): print("%s 说: 我 %d 岁,%d" %(self.name,self.age,self.__weight)) # 实例化类 p = people('haha',10,30) p.speak()
父类的 私有属性 和 私有方法
- 子类对象 不能 在自己的方法内部,直接 访问 父类的 私有属性 或 私有方法
- 子类对象 可以通过 父类 的 公有方法 间接 访问到 私有属性 或 私有方法
- 私有属性、方法 是对象的隐私,不对外公开,外界 以及 子类 都不能直接访问
- 私有属性、方法 通常用于做一些内部的事情
'''B 的对象不能直接访问 __num2 属性 B 的对象不能在 demo 方法内访问 __num2 属性 B 的对象可以在 demo 方法内,调用父类的 test 方法 父类的 test 方法内部,能够访问 __num2 属性和 __test 方法''' class A(): def __init__(self): self.num1 = 100 self.__num2 = 200 def __test(self): print("私有方法 %d %d" % (self.num1, self.__num2)) def test(self): print("父类的公有方法 %d" % self.__num2) self.__test() class B(A): def demo(self): # 1. 在子类的对象方法中,不能访问父类的私有属性 # print("访问父类的私有属性 %d" % self.__num2) # 2. 在子类的对象方法中,不能调用父类的私有方法 # self.__test() # 3. 访问父类的公有属性 print("子类方法 %d" % self.num1) # 4. 调用父类的公有方法 self.test() pass # 创建一个子类对象 b = B() print(b) b.demo() # 在外界不能直接访问对象的私有属性/调用私有方法 # print(b.__num2) # b.__test() # 在外界访问父类的公有属性/调用公有方法 # print(b.num1) # b.test()
多态
多态 不同的 子类对象 调用相同的 父类方法,产生不同的执行结果
- 多态 可以 增加代码的灵活度
- 以 继承 和 重写父类方法 为前提
- 是调用方法的技巧,不会影响到类的内部设计
多态性(polymorphisn)是允许你将父对象设置成为和一个或更多的他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作。简单的说,就是一句话:允许将子类类型的指针赋值给父类类型的指针。
那么,多态的作用是什么呢?我们知道,封装可以隐藏实现细节,使得代码模块化;继承可以扩展已存在的代码模块(类);它们的目的都是为了——代码重用。而多态则是为了实现另一个目的——接口重用!多态的作用,就是为了类在继承和派生的时候,保证使用“家谱”中任一类的实例的某一属性时的正确调用。
class Animal: def __init__(self, name): self.name = name def talk(self): pass #raise NotImplementedError("Subclass must implement abstract method") @staticmethod def animal_talk(obj): obj.talk() class Cat(Animal): def talk(self): print('Meow!') class Dog(Animal): def talk(self): print('Woof! Woof!') d = Dog("花花") #d.talk() c = Cat("flower") #c.talk() # # def animal_talk(obj): # obj.talk() Animal.animal_talk(c) Animal.animal_talk(d)
多态练习
在 Dog
类中封装方法 game,
普通狗只是简单的玩耍
定义 XiaoTianDog
继承自 Dog
,并且重写 game
方法,哮天犬需要在天上玩耍
定义 Person
类,并且封装一个 和狗玩 的方法
在方法内部,直接让 狗对象 调用 game
方法
'''在 Dog 类中封装方法 game 普通狗只是简单的玩耍 定义 XiaoTianDog 继承自 Dog,并且重写 game 方法 哮天犬需要在天上玩耍 定义 Person 类,并且封装一个 和狗玩 的方法 在方法内部,直接让 狗对象 调用 game 方法''' class Dog(): def __init__(self,name): self.name = name def game(self): print("%s 蹦蹦跳跳的玩耍..." % self.name) class Xiaotianquan(Dog): def game(self): print("%s 飞到天上去玩耍" % self.name) class Person(): def __init__(self,name): self.name = name def game_with_dog(self,dog): print(f"{self.name}和{dog.name}快乐的玩耍") # 让狗玩耍 dog.game() # 1. 创建一个狗对象 duoduo = Dog("多多") #子类哮天犬对象 shenquan = Xiaotianquan("神犬") shenquan.game() # 2. 创建一个小明对象 xiaoming = Person("小明") # 3. 让小明调用和狗玩的方法 xiaoming.game_with_dog(duoduo)
对象封装练习:
''' 房子(House) 有 户型、总面积 和 家具名称列表 新房子没有任何的家具 家具(HouseItem) 有 名字 和 占地面积,其中 席梦思(bed) 占地 4 平米 衣柜(chest) 占地 2 平米 餐桌(table) 占地 1.5 平米 将以上三件 家具 添加 到 房子 中 打印房子时,要求输出:户型、总面积、剩余面积、家具名称列表 ''' #家具类 class HouseIem(): def __init__(self,name,area): """ :param name: 家具名称 :param area:占地面积 """ self.name = name self.area = area def __str__(self): return f"{self.name}占地面积为{self.area}" #创建房子 class House(): def __init__(self,house_type,area): self.house_type = house_type self.area = area #剩余面积要和总面积一致 self.free_area = area # 默认没有任何的家具 self.item_list = [] def __str__(self): # Python 能够自动的将一对括号内部的代码连接在一起 return ("户型:%s\n总面积:%.2f[剩余:%.2f]\n家具:%s" % (self.house_type, self.area, self.free_area, self.item_list)) def add_item(self,item): print(f"要添加{item}") # 1. 判断家具面积是否大于剩余面积 if item.area > self.free_area: print("%s 的面积太大,不能添加到房子中" % item.name) return # 2. 将家具的名称追加到名称列表中 self.item_list.append(item.name) # 3. 计算剩余面积 self.free_area -= item.area # 1,创建家具 bed = HouseIem("席梦思", 4) chest = HouseIem("衣柜", 2) table = HouseIem("餐桌", 1.5) print(bed) print(chest) print(table) # 2. 创建房子对象 my_home = House("两室一厅", 60) my_home.add_item(bed) my_home.add_item(chest) my_home.add_item(table) print(my_home)
类的专有方法:
- __init__ : 构造函数,在生成对象时调用
- __del__ : 析构函数,释放对象时使用
- __repr__ : 打印,转换
- __setitem__ : 按照索引赋值
- __getitem__: 按照索引获取值
- __len__: 获得长度
- __cmp__: 比较运算
- __call__: 函数调用
- __add__: 加运算
- __sub__: 减运算
- __mul__: 乘运算
- __div__: 除运算
- __mod__: 求余运算
- __pow__: 乘方
class Vector: def __init__(self, a, b): self.a = a self.b = b def __str__(self): return 'Vector (%d, %d)' % (self.a, self.b) def __add__(self,other): return Vector(self.a + other.a, self.b + other.b) v1 = Vector(2,10) v2 = Vector(5,-2) print (v1 + v2)
类的内置属性
- __dict__ : 类的属性(包含一个字典,由类的数据属性组成)
- __doc__ :类的文档字符串
- __name__: 类名
- __module__: 类定义所在的模块(类的全名是'__main__.className',如果类位于一个导入模块mymod中,那么className.__module__ 等于 mymod)
- __bases__ : 类的所有父类构成元素(包含了一个由所有父类组成的元组)
class Employee: '所有员工的基类' empCount = 0 def __init__(self,name,salary): self.name = name self.salary = salary Employee.empCount+=1 def displayCount(self): print(Employee.empCount) def displayEmployee(self): print(self.name,self.salary) test1 = Employee("小米",2000) test1.displayEmployee() print(Employee.empCount) print (Employee.__doc__) print (Employee.__name__) print (Employee.__module__) print (Employee.__bases__) print (Employee.__dict__)
组合
#组合:一个类的对象是另外一个类对象的属性 # 学生类,姓名,性别,年龄,学号,班级 class Student: def __init__(self,name,sex,age,number,clas): self.name = name self.sex = sex self.age = age self.number = number self.clas = clas #班级类,班级名,开班日期,当前讲师 class Clas: def __init__(self,cname,begint,teacher): self.cname = cname self.begint = begint self.teacher = teacher cl1 = Clas('语文班','19号','f') cl2 = Clas('数学班','20号','r') xiaoming = Student('小明','男',12,42,cl1) print(xiaoming.clas,cl1) print(cl1.begint) print(xiaoming.clas.begint)