面向对象的python编程方法进阶:方法相关内容、继承、和多态
一、方法:
方法包括:
普通方法、静态方法和类方法,三种方法在内存中都归属于类,区别在于调用方式不同。
(1)、实例方法:
通常情况下,类中的方法默认是实例方法,在定义的时候不需要使用使用特殊的关键字进行标识。
1、基本知识:
①实例方法至少有一个参数,默认该参数的名字为“self”(也可以是其他的),若有其他参数,可以跟在该参数后面。
②实例方法有两种调用方式:
第一种:通过实例对象调用,该方法不需要手动传递实例参数,编译器在后期加载的时候会把实例加载至实例参数。
第二种:通过类名称调用,该方法需要手动传递实例参数。
注意: 从上面的两种调用方法我们可以知道,实例方法被调用时要向它的第一个参数传递实例对象。
class Cat(object): def eat(self): print("小猫在吃饭") def drink(self): print("小猫在喝水") def sleep(self): print("小猫在睡觉") def introduce1(self): name="小花猫" age=2 print("我是一只猫,名字是:%s,年龄是:%s" % (name,age)) def introduce2(self,name1,age1): print("我是一只猫,名字是:%s,年龄是:%s" % (name1,age1)) xiaomao=Cat() #创建一个小猫对象 xiaomao.eat() #类对象名.方法名() xiaomao.drink() #通过实例对象调用 xiaomao.sleep() #通过实例对象调用 xiaomao.introduce1() #通过实例对象调用,只有一个参数self,可写可不写,函数输出内容固定 xiaomao.introduce2("小花猫",2) #通过类名称调用,必须必须输入实例参数,可实现函数输出内容的更改 print("------------------------") tom=Cat() tom.eat() tom.drink() tom.sleep() tom.introduce1() tom.introduce2("Tom",3)
输出结果:
小猫在吃饭
小猫在喝水
小猫在睡觉
我是一只猫,名字是:小花猫,年龄是:2
我是一只猫,名字是:小花猫,年龄是:2
------------------------
小猫在吃饭
小猫在喝水
小猫在睡觉
我是一只猫,名字是:小花猫,年龄是:2
我是一只猫,名字是:Tom,年龄是:3
(2)、类方法:
1、基本知识:
①类方法要使用装饰器@classmethod来修饰,一般情况,第一参数默认命名为cls(cls=class,可以是别的名字)
②类方法有两种调用方式:
第一种:使用类的名称调用
第二种:使用实例调用
注意:从以上的两种调用方式,我们需要知道,类方法调用的时候,会向他的第一个参数传递类的名称。
类方法可以不在创建实例对象之前调用,而实例方法不行。
class Tool(object): num=0 #此时num是一个类属性 def __init__(self,name): #实例方法 self.name=name #实例属性 #Tool.num+=1 #通过类型可以访问类属性 #想要调用类方法,可以通过实例对象.类方法()也可以通过类名.类方法() self.add_1() @classmethod def add_1(cls): #方法,cls一定指向当前类 cls.num+=1 @classmethod def print_tool_num(cls): print("当前工具数的个数为:%d"%cls.num) tieqiao=Tool("铁锹") chutou=Tool("锄头") saoba=Tool("扫把") print(Tool.num) Tool.print_tool_num() #通过类名调用,可在不创建实例对象之前调用 tieqiao.print_tool_num() #通过类的实例对象调用
输出结果:
3
当前工具数的个数为:3
当前工具数的个数为:3
(3)、静态方法:
1、基本知识
①静态函数通过装饰器@staticmethod修饰
②静态函数的调用有以下两种方式:
第一种:实例调用
第二种:类名称调用
注意: 我们需要知道在调用静态函数时不会向函数传递任何特殊参数,在上面的实例方法和类方法中都有自己的特殊参数,用来和类或者对 象进行捆绑使用,而静态方法是没有的。虽然它没有参数,但是不意味着它没有参数,它也是可以拥有普通参数的,比如字符串类型。
静态方法没有类似 self、cls 这样的特殊参数,因此 Python 解释器不会对它包含的参数做任何类或对象的绑定。也正因为如此,类的静态方 法中无法调用任何类属性和类方法。
class Calculator(object): def __init__(self): #定义两个默认值 self.num1=0 self.num2=0 @staticmethod def show_menu(): #因为打印菜单功能方法并不需要self指向对象,所以就考虑使用静态方法 print("1.加法") print("2.减法") print("3.退出") def get_nums(self): self.num1=int(input("请输入第一个数:")) self.num2=int(input("请输入第二个数:")) def add(self): print(self.num1+self.num2) def min(self): print(self.num1+self.num2) def run(self): while True: self.show_menu() op=input("请输入要进行的操作") if op=="1": self.get_nums() self.add() elif op=="2": self.get_nums() self.min() elif op=="3": break #定义的静态方法可以用实例对象化进行调用,即 实例对象.静态方法() cal=Calculator() cal.run() #通过类名调用 Calculator.show_()
输出结果:
1.加法
2.减法
3.退出
请输入要进行的操作
(之后的操作请自行尝试)
总结:
- 实例方法(对象方法):随着实例属性的改变而改变
- 类方法:类属性的值,不随实例属性的变化而变化
- 静态方法:不可以访问类属性,直接输出传入方法的值
class Student: name="jom" age=21 @staticmethod def staticshow(): print(Student.name,Student.age) @classmethod def classshow(cls): print(cls.name, cls.age) def instanceshow(self): print(self.name, self.age) Student.classshow()#类的名称调用 #输出jom 21 s=Student() #实例调用 print("使用静态方法:") s.staticshow() #输出jom 21 s.age=11 #无效修改 print("修改age后再次使用静态方法:") s.staticshow() #输出jom 21 Student.age=11 #有效修改 print("修改age后再次使用静态方法:") s.staticshow() #输出jom 11 print("-------------------------") Student.classshow()#类的名称调用 #输出jom 11 p=Student() print("使用类方法:") p.classshow() #使用实例调用 #输出jom 11 p.name="tom" #无效修改 print("修改name后再次使用类方法:") p.classshow() #输出jom 11 Student.name="tom" print("修改name后再次使用类方法:") p.classshow() #输出tom 11 print("---------------------") q=Student() print("使用实例方法:") q.instanceshow() #输出tom 11 q.name="alen" print("修改name后再次使用实例方法:") q.instanceshow() #输出alen 11 Student.name="tom"#无效修改 q.instanceshow() #输出alen 11
二、:继承和多态
面向对象编程有3个特征:封装、继承、多态,这次主要学习继承和多态。
(1)、继承:继承允许我们定义继承另一个类的所有方法和属性的类。父类是继承的类,也称为基类。子类是从另一个类继承的类,也称为派生类。
继承分为单继承和多继承。
1、单继承:一个子类只继承了一个父类。
①写到父类
class Animal: def eat(self): print("吃饭...") def drink(self): print("喝水...") def sleep(self): print("睡觉...") def info(self): print("名字是:" + self.name + ", 年龄是" + str(self.age)) class Dog(Animal): def __init__(self, name, age): self.name = name self.age = age dog = Dog("小黑", 2) dog.eat() dog.drink() dog.sleep() dog.info()
输出:
吃饭...
喝水...
睡觉...
名字是:小黑, 年龄是2
从上述的运行来看,虽然Animal类中没有name属性、age属性,但是可以在子类中添加这2个属性,只要方法被子类继承后,通过子类的对象调用时可以直接使用子类的属性,但是,要注意如果在父类Animal中如果没有name、age那么也就意味着不能直接创建Animal类的实例对象,否则当通过Animal的实例对象调用info方法时就会出现找不到name、age属性的问题。
②写到子类
class Animal: def eat(self): print("吃饭...") def drink(self): print("喝水...") def sleep(self): print("睡觉...") def info(self): print("名字是:" + self.name + ", 年龄是" + str(self.age)) class Dog(Animal): def __init__(self, name, age): self.name = name self.age = age dog = Dog("小黑", 2) dog.eat() dog.drink() dog.sleep() dog.info()
输出:
吃饭...
喝水...
睡觉...
名字是:小黑, 年龄是2
可以看到,第2种写法仅仅是将方法info移动到类Dog中,那你可能要问了,即使这两种写法不同,但有什么区别吗?
答:如果是采用第一种即写到父类那种,此时info方法是在父类Animal中,如果以后这个程序代码还有添加新的子类例如Cat类,那么Cat类中也会继承这个方法,那么如果子类Cat类中没有定义name、age属性就不能通过子类的对象调用info方法;如果是采用第二种即写到子类那种,好处是info方法只会在Dog类创建的实例对象中,即便定义了其它的子类继承Animal,也不会导致这些新的子类额外的添加了info方法
总结:如果一个方法在多个子类中用到,那么就定义在父类的方法中,否则就定义在子类中
2、多继承:如果一个类继承了多个父类,那么这就是多继承。
格式定义:单继承时在类()中写1个父类的名字,那么多继承就需要在()中写多个父类的名字而且用英文逗号,进行分割。
class A: pass class B: pass class C(A, B): # 继承了A、B类 pass
示例:
多继承使定义子类的功能更加灵活
class Camera: def take_photo(self): print("正在拍照...") class MP3: def play_music(self): print("正在播放音乐...") class Telephone(Camera, MP3): def call(self): print("正在打电话...") def answer(self): print("正在接电话...") iphone = Telephone() iphone.call() iphone.answer() iphone.take_photo() iphone.play_music()
此代码中Telephone继承了play_music和take_photo的功能
输出:
正在打电话...
正在接电话...
正在拍照...
正在播放音乐...
(2)重写:
1、引入:
我们知道一个子类如果继承了父类,那么当通过子类对象去调用一个方法时,如果子类对象中没有此方法,那么就会到继承的父类中查询,如 果查询到有则进行调用,但是有时,我们发现子类继承的父类的方法不能100%满足子类对象的需求,则此时就需要在子类中定义一个与父类 相同的名字的方法,此时子类对象调用这个方法时即使父类中有,但依然不会调用,而是调用子类中的方法。
2、定义:我们把子类中定义了与父类中相同名字的方法,叫做重写(大白话就是:子类的方法覆盖了父类中的同名方法)
3、示例:
class Father(object): def play_game(self): print("父类中的play_game") def drink(self): print("父类中的drink方法")
class Son(Father): def play_game(self): print("子类中的play_game") father = Father() father.play_game() # 调用父类中的方法,因为对象是父类创建的 son = Son() son.play_game() # 调用子类中的方法,因为在子类中重写了play_game方法 son.drink() # 调用父类中的方法,因为子类中并没有重写此方法
输出:
父类中的play_game
子类中的play_game
父类中的drink方法
如果父类中的方法在子类继承时发现并不符合子类的需求,此时我们在子类中重写这个方法即可
切记:不要直接在父类中修改此方法,如果将父类中的方法改成你子类的功能,虽然子类创建的对象可以100%满足要求,但你并不能保证其它继承这个父类的子类也需要同样的功能,所以在以后的开发工作中,一个父类定义好了之后,就不要轻易的修改,否则继承它的子类都要进行修改,这个工作量是非常大的,请不要这么做。
(3)super方法:
如果父类中的方法在派生的子类中不能满足其需求的话,可以在子类中通过重写解决这个问题,但是很多情况下,父类中的方法并不是全部一点都 不能用,即子类的需求往往是在父类方法实现的功能基础上提出了更多的需求而已,此时如果我们在子类中重写此方法时就会发现出现了很多冗余 的代码,这个问题该怎么解决呢?
答:在子类重写的方法中通过调用父类中被重写的方法
示例:
class Father(object): def play_game(self): print("父类中的play_game") class Son(Father): def play_game(self): super().play_game() # 先调用父类被重写的方法 print("子类中的play_game") # 然后再添加子类需要的新功能 son = Son() son.play_game() # 调用子类中的方法,因为在子类中重写了play_game方法
输出:
父类中的play_game
子类中的play_game
示例:
class Father(object): def __init__(self, name, age): self.name = name self.age = age def __str__(self): return "%s的年龄是:%d" % (self.name, self.age) class Son(Father): def __init__(self, name, age, collage): super().__init__(name, age) self.collage = collage def __str__(self): return "%s的年龄是:%d,他的学历是:%s" % (self.name, self.age, self.collage) father = Father("父亲", 50) print(father) son = Son("儿子", 18, "大学") print(son)
输出:
父亲的年龄是:50
儿子的年龄是:18,他的学历是:大学
总结:如果想要在子类方法中调用被重写的父类方法就可以使用super().父类方法名()
(2)、多态:
1、定义:
多态从字面意思来看,就是多种形态的意思,在python中它的实际功能是,如果一个变量存储了某一个实例对象的引用,且通过这个变量调用
指向的对象中的某个方法,此时如果变量指向的对象是子类创建的那么就调用子类中的方法,如果是父类创建的对象那么就调用父类的方法。
示例:
class Dog(object): def bark(self): print("狗汪汪叫...") class LangDog(Dog): def bark(self): #继承,重写 print("狼狗震耳欲聋的叫...") class ZangAo(Dog): #继承 pass class Person(object): def pk_dog(self, dog): print("人 用力的向狗进行了攻击...") dog.bark() laowang = Person() dog1 = Dog() dog2 = LangDog() dog3 = ZangAo() laowang.pk_dog(dog1) laowang.pk_dog(dog2) laowang.pk_dog(dog3)
输出:
人 用力的向狗进行了攻击...
狗汪汪叫...
人 用力的向狗进行了攻击...
狼狗震耳欲聋的叫...
人 用力的向狗进行了攻击...
狗汪汪叫...
总结:想实现多态需要的条件:有继承、有重写

浙公网安备 33010602011771号