💦初识面向对象
什么是面向对象?为什么要引入面向对象?
首先,早期计算机编程语言都是面向过程的,程序即由数据和算法构成,数据可以构成复杂的数据结构,算法也是由上到下的复杂逻辑控制,这是一种将数据与操作算法分离开的编程思想。😲总是就是很麻烦的意思。
而面向对象中的对象,将数据和方法包装起来,它内部的数据和算法由开发者设计,我们只需调用接口就行了,不需关心实际对象内部的复杂逻辑😄。
☁️对象
现实生活中我们能见到的、能触碰到的人和事物都是对象,如你、我、猫、汽车、树等等。“万物皆是对象”,但咱就没有对象😭。对象可以是有形的,也可以是无形的。我们可以将对象简单地处理为两个部分——属性和行为。
属性
对象具有属性,可以称为状态,也可以称为变量。例如👉每个人都有名字、性别、身高、体重等。再直白一点就是,在游戏里有时候会给角色的属性加点😆,像力量、体力、速度、敏捷、魔法等等。
而同一类中有许多不同的对象,进而这些的对象的属性值也并不相同。正如这个世界上70亿人就是一个大类,我们都是其中一个对象罢了。
行为
对象具有行为,也可以称为方法。例如,我们身为人这一类中的对象,我们要吃饭、睡觉、打豆豆。这些就是属于对象的行为。
面向对象编程将完成某个功能的代码块定义为方法,方法可以被其它程序调用,也可以自己调用,例如,我可以自己吃饭,你也可以喂我吃饭😋。
同时,每个对象的行为也是不一样的,例如一般常人都12点之前就睡觉了,而我不知不觉就熬到了一两点。。。。
☁️类
类是相同类似对象的统称。例如👉狗是一个类,属于它的对象有:二哈、金毛、中华田园犬等等。手机也可以是一个类,属于它的对象有:华为、三星、苹果、小米等等。
类是对象的属性和行为被进一步封装的模板,不同的类之间的属性和行为都是不同的。就好比人的行为能跟狗的行为一样吗?答案…当然是否定的。
💦Python与面向对象
☁️基本术语
- 类(class):用来描述具有相同的属性和方法的对象的集合。对象是类的实例(具体例子)。
- 实例化:创建一个类的实例,一个类的具体对象。
- 实例变量:定义在方法中的变量,只作用于当前实例的类。
- . 类变量:类变量定义在类中且在函数体之外,常不作为实例变量使用。
- 对象:通过类定义的数据结构实例。对象包括两个数据成员(类变量和实例变量)和方法。
- 数据成员:类变量或实例变量,用于处理类及其实例对象的相关数据。
- 方法:类中定义的函数。
- 继承:指一个派生类继承基类的字段和方法。继承也允许把一个派生类的对象作为一个基类对象对待。
- 方法重写:如果从父类继承的方法不能满足子类的需求,可以对其进行改写,这个过程较方法的覆盖,也称为方法的重写。
☁️类的定义
类必须在被定义后才能使用,定义一个类也就是定义这一类对象的模板。Python中用 c l a s s class class关键字来声明一个类, c l a s s class class中有成员属性和成员方法。例如👇:
class Empty:
pass
empty = Empty()
print(type(empty))
#<class '__main__.Empty'>
其中, e m p t y empty empty就是一个对象,类的实例化,同时在内存中为这个对象分配了内存空间。
在创建实例的时候,很多类可能都需要有特定的初始状态。所以,一个类可以定义一个特殊的方法,叫做构造函数。在Python中,构造函数就是类的__init__方法(init前后都有两个连续的短下划线)。例如👇:
class Dog:
def __init__(self):
print("汪汪汪!")
dog = Dog()
#汪汪汪
注意❗️:当一个类定义了__init__方法,类在实例化时会自动调用__init__方法,用于创建新的类实例。在上面的例子中,新的实例dog被创建,同时又执行了构造方法,也就是运行了__init__函数。还有一点需要注意的是,构造方法的返回值必须是None。在定义构造方法的时候解释器不会报错,但是在实例化的时候Python会输出错误提示“TypeError: init() should return None”。
在构造方法中,我们可以初始化一些属性,例如👇:
class Dog:
def __init__(self,name):
self.name = name;
self.age = 3
dog = Dog("旺财")
print(dog.name)
#旺财
print(dog.age)
#3
属性(或者叫成员变量、类变量)必须要使用self加上点的方式赋值,不能直接定义变量。直接定义的变量的声明周期只会在函数内,函数执行完变量就会被销毁。
其实函数__init__的第一个参数self指的就是实例本身,可以理解为对实例的属性赋值,相当于实例的初始化。Python在调用__init__函数时会自动地添加实例作为函数的第一个参数。(这部分非常重要)
在类中定义的函数我们称为方法。自己定义成员方法也很简单,例如👇:
class Dog:
def __init__(self,name):
self.name = name
def play(self):
print("汪汪汪!我是", self.name)
dog = Dog("旺财")
dog.play()
#汪汪汪!我是 旺财
方法除了一定要定义在类里面并且第一个参数必须是“self”(也可以叫做其它的名字)外,其它和函数的定义没有任何区别。
类中还有私有属性,用来限制外部对属性的修改。例如👇:
class Dog:
def __init__(self,name):
self.__name = name
def play(self):
print("汪汪汪!我是", self.__name)
dog = Dog("旺财")
dog.play()
#汪汪汪!我是 旺财
print(dog.__name)
#AttributeError: 'Dog' object has no attribute '__name'
那么我们如何能修改到私有属性呢?首先,明确私有属性是为了限制外部的访问(也就是类的外面),所以我们在类里面修改就行了,也就是在类里面再定义一个方法,用来修改私有属性。例如👇:
class Dog:
def __init__(self,name):
self.__name = name
def play(self):
print("汪汪汪!我是", self.__name)
def set_name(self,name):
self.__name = name
dog = Dog("旺财")
dog.play()
#汪汪汪!我是 旺财
dog.set_name("二哈")
dog.play()
#汪汪汪!我是 二哈
那么,既然有私有属性,那么就一定有私有方法。同样地,和私有属性一样,私有方法只能在类的内部被调用,实例不能直接调用。例如👇:
class Dog:
def __init__(self):
pass
def __say(self,name):
print(name)
def play(self):
self.__say("汪汪汪!")
dog = Dog()
dog.play()
#汪汪汪
dog._say("旺财")
#AttributeError: 'Dog' object has no attribute '__say'
也就是说,__say函数只能被类中的其它函数调用,不能从外部调入。
💦继承
继承是一种对类进行分层级划分的概念。它的基本思想是在一个类的基础上制定出一个新的类,这个新的类不仅可以继承原来类的属性和方法,还可以增加新的属性和方法。原来的类被称为父类,新的类被称为子类。Python中一个子类可以继承多个父类。其语法如下👇:
class SubClass(BaseClass1,BaseClass2):
语法块
定义要继承哪个父类,只需在定义子类名字后面的括号中填入父类的名字,如果有多个父类,则用逗号隔开。例如👇:
class Animal:
def __init__(self, name):
self.name = name
def play(self):
print("我是", self.name)
class Dog(Animal):
pass
dog = Dog("旺财")
dog.play()
#我是 旺财
有两点需要注意❗️:
- 在继承中,若子类定义了构造方法,则父类的构造方法__init__不会被自动调用,需要在子类的构造方法中用 s u p e r 函数 super函数 super函数专门调用。例如👇:
class Animal:
def __init__(self, name):
self.name = name
def play(self):
print("我是", self.name)
class Dog(Animal):
def __init__(self):
super(Dog , self).__init__("旺财")
dog = Dog()
dog.play()
#我是 旺财
- 子类不能继承父类中的私有方法,也不能调用父类的私有方法。
💦多态
继承可以帮助我们重复使用代码,但是有时候子类的行为不一定完全和父类一样。例如👇:
class Animal:
def say(self):
print('Animal')
class Dog(Animal):
pass
class Cat(Animal):
pass
dog = Dog()
dog.say()
#Animal
cat = Cat()
cat.say()
#Animal
我们想让dog和cat在调用say方法时能输出不同的内容,那么该如何做呢?看下面👇:
class Animal:
def say(self):
print('Animal')
class Dog(Animal):
def say(self):
print('dog')
class Cat(Animal):
def say(self):
print('cat')
dog = Dog()
dog.say()
#dog
cat = Cat()
cat.say()
#cat
在上面的代码中,Dog和Cat分别调用了各自的say方法。
那么,当子类和父类存在相同的方法时,子类的方法会覆盖父类的方法,这样代码在运行时总是会调用子类的方法,这就是多态。
判断一个实例是不是某个对象可以使用isinstance函数,例如👇:
class Animal:
def say(self):
print('Animal')
class Dog(Animal):
def say(self):
print('dog')
class Cat(Animal):
def say(self):
print('cat')
dog = Dog()
cat = Cat()
print(isinstance(dog,Dog))
#True
print(isinstance(dog,Animal))
#True
print(isinstance(cat,Cat))
#True
print(isinstance(cat,Animal))
#True
💦知识拓展
☁️类变量和实例变量
类变量和实例变量有什么区别?👉:类变量不需要实例化就能直接使用。例如👇:
class Animal:
name = '动物'
print(Animal.name)
#动物
类变量在实例中也是可以被调用的,例如👇:
class Animal:
name = '动物'
dog = Animal()
cat = Animal()
print(dog.name)
#动物
print(cat.name)
#动物
Animal.name = '哺乳动物'
print(dog.name)
#哺乳动物
print(cat.name)
#哺乳动物
但是实例不能修改类变量,如果实例对类变量进行修改,Python解释器就会新建一个同名的成员变量来代理实例中的类变量。