Python基础之面向对象

一、类(class)与实例

首先了解一下封装的概念:在函数的学习中我们已经接触了封装,把常用的代码块打包成一个函数,这是一种封装,是语句层面的封装,我们定义的列表、字典等数据,也属于一种封装,即把分散的数据打包到列表或字典中,这是数据层面的封装;

在Python中一切皆对象,所有数据类型都可视为对象,面向对象编程,也是一种封装的思想,不过要比以上两种封装更先进,它可以更好地模拟真实世界里的事物(将其视为对象),并把描述特征的数据和代码块(函数)封装到一起。

int # 为int对象
float # 为float对象
list # 为list对象
dict # 为dict对象
a = 1 # a为一个int对象

 类是自定义对象,抽象的模板,创建实例的模板,比如:人类,动物类,汽车类,飞机类,当然还可以细分为对应子类如动物下面的猫类,狗类

定义一个类

class Cat:
    def __init__(self,name,weight,color,ele_color):
	    	self.name = name
        self.weight = weight
        self.color = color
        self.ele_color= ele_color
    def cat_run(self):
	    	print(f"{self.name}正在奔跑")
    def cat_eat(self):
        print(f"{self.name}正在吃小鱼干")
    def cat_sleep(self):
        print(f"{self.name}正在睡觉")
		def cat_clear_hair(self):
				print(f"{self.name}正在清理它{self.color}的毛发")

 

上述代码中就通过class关键字定义了一个Cat类,该类中包含属性:name, weight, color, ele_color;包含的方法(对象行为):cat_run( ), cat_eat( ), cat_sleep( ),cat_clear_hair( )。该Cat类就像一个模板,通过调用这个模板可以源源不断创造出不同的实例来

面向对象中,常用术语包括:

  • 类:可以理解是一个模板,通过它可以创建出无数个具体实例。比如,前面编写的 Cat 表示的只是猫这个物种,通过它可以创建出无数个实例来代表各种不同特征的猫(这一过程又称为类的实例化)。
  • 对象:类并不能直接使用,通过类创建出的实例(又称对象)才能使用。这有点像工厂设计图纸和产品的关系,图纸本身(类)并不能为人们使用,通过图纸创建出的产品(对象)才能使用。
  • 属性:类中的所有变量称为属性。例如,Cat 这个类中,name、weight、color、ele_color都是这个类拥有的属性。
  • 方法:类中的所有函数通常称为方法。不过,和函数不同的是,类方法至少要包含一个 self 参数(必须是第一个参数)。例如,Cat 类中,cat_run()、cat_eat()、cat_sleep()、cat_clear_hair() 都是这个类所拥有的方法,类方法无法单独使用,只能和类的对象一起使用。

类的定义

定义一个类使用 class 关键字实现,其基本语法格式如下

class 类名:  # 和变量名一样,类名本质上就是一个标识符,起类名时同样见名知意
    属性(数量>=0) 
    方法(数量>=0) 

 类名注意:多个单词构成类名,建议每个单词的首字母大写,其它字母小写

Class LoginPage:
    pass

Class MainPage:
    pass

类名后面要加 “ :”,即告诉 Python 解释器,下面要开始设计类的内部功能了,也就是编写类属性和方法(类属性指的就是包含在类中的变量;而类中的方法指的是包含在类中的函数);

注意:一个类中的属性与方法要保持统一的缩进(统一缩进4空格)

尝试创建一个Dog类

class Dog:

		#类属性:简单地理解,定义在各个类方法之外(包含在类中)的变量为类变量(或者类属性)

    eles_num = 2  # 类属性,眼睛数量
    legs_num = 4  # 类属性,腿数量


    def __init__(self, name, age, hair_color, ele_color):  # 构造方法,用于类实例化时传入实例参数
        self.name = name  # 实例属性
        self.age = age
        self.hair_color = hair_color
        self.ele_color = ele_color

    def dog_run(self):  # 实例方法
        print(f"小狗{self.name},正挥动它{self.legs_num}条小腿快速奔跑;")

    def dog_blink_eles(self): # 实例方法
        print(f"小狗{self.name},正眨动它{self.eles_num}只{self.ele_color}的眼睛;")

上述 Dog 类中,eles_num ,legs_num是 Dog类的类属性(即所有Dog所共有的,相同的特征) 同时在创建类时,我们可以手动添加一个__ init __() 方法,该方法是一个特殊的类实例方法,称为构造方法(注意:注意,此方法的方法名中,开头和结尾各有 2 个下划线,且中间不能有空格),另外,__init __() 方法可以包含多个参数,但必须包含一个名为 self 的参数,且必须作为第一个参数,即类的构造方法最少也要有一个 self 参数。

每当创建一个类的实例对象时,Python解释器都会自动调用构造方法;

类的实例化(调用类)

对定义好的类进行实例化,语法如下:

[变量名] = 类名(参数)  # 赋值变量为非必须

 

注:实例化时,需要输入的实参,由__ init __(self, 参数1,参数2)构造方法中的参数决定,在实例化时,self参数忽略,不需要实参(self可理解为一个固定的标签,不需要我们关注)

对上述Cat类进行实例化

cat_niki = Cat('niki', 3, '白色' , '蓝色')  #通过调用Cat类,传入对应参数name, age, color, ele_color的实参
																						#并赋值给变量cat_niki,实现实例化,即创建一个cat_niki实例对象

 实例化时参数传递关系如下(注意:self 参数是特殊参数,不需要手动传值,Python 会自动传给它值)

Python基础之面向对象_第1张图片

定义的类只有进行实例化,也就是使用该类创建对象之后,才能使用,跟我们前面学的函数是一个道理

实例对象的使用(即类实例化后的使用)

访问属性或方法语法如下

实例对象.属性名
实例对象.方法名(参数)

例:访问Cat的color属性

cat_niki = Cat('niki', 3, '白色' , '蓝色') #调用Cat类传入实参进行实例化
niki_color = cat_niki.color # 通过 实例对象.属性名 访问获取这个cat_niki的color属性
print(niki_color)  # 输出结果为白色

 例:访问Cat的 cat_clear_hair()方法

cat_niki = Cat('niki', 3, '白色' , '蓝色') #调用Cat类传入实参进行实例化
cat_niki.cat_clear_hair() # 通过 实例对象.方法名(参数) 访问cat_clear_hair这个实例方法
                          # 结果:niki正在清理它白色的毛发

给实例对象动态添加属性 

cat_niki = Cat('niki', 3, '白色' , '蓝色') #调用Cat类传入实参进行实例化
cat_niki.body_length = 0.3  # 给实例cat_niki添加实例属性body_length为0.3米
print(cat_niki.body_length) # 输出 0.3

修改实例对象的属性值

cat_niki = Cat('niki', 3, '白色' , '蓝色')
cat_niki.weight = 2  # 对属性weight重新赋值2
print(cat_niki.weight) # 输出 2

 

self参数的作用

在我们定义类的过程中,无论构造方法,还是实例方法,都以self为第一个参数,实际上Python并未规定类中的构造方法或实例方法第一个参数必须叫self,我们约定第一个参数self是一种俗成习惯,把self这个参数改成this或别的参数程序也不影响运行,遵守这个 约定,可以代码具有更高的可读性(即大家都认识这个self,不认识你这个自定义的this);

self的作用即:指向实例本身

例如上面的Dog类,我们可通过Dog类创建无数个不同的实例,例如

class Dog:

    eles_num = 2  # 类属性,眼睛数量
    legs_num = 4  # 类属性,腿数量


    def __init__(self, name, age, hair_color, ele_color):
        self.name = name
        self.age = age
        self.hair_color = hair_color
        self.ele_color = ele_color

    def dog_run(self):
        print(f"小狗{self.name},正挥动它{self.legs_num}条小腿快速奔跑;")

    def dog_blink_eles(self):
        print(f"小狗{self.name},正眨动它{self.eles_num}只{self.ele_color}的眼睛;")


Jamis = Dog('Jamis', 3, '黄色', '黑色')  #创建一个名叫Jamies小狗的实例
Tom = Dog('Tom', 5, '黑色', '褐色')      #创建一个名叫Tom小狗的实例

上述代码,创建两个不同的小狗对象,它们都有各自的name, age, hair_color属性,以及各自的dog_run(), dog_blink_eles()方法,那把Jamies的name属性以及dog_run方法与Tom的区分开来,就需要依靠self这个参数,当实例化Dog创建一个对象时,Python 会自动绑定类方法的第一个参数self指向调用该方法的对象(实例对象),实例化时会开辟一块新的内存空间,每个实例都是开辟一个新的空间来存放该对象,每个实例的self都会指向对应的内存地址,如下面代码

class Dog:

    eles_num = 2  # 类属性,眼睛数量
    legs_num = 4  # 类属性,腿数量


    def __init__(self, name, age, hair_color, ele_color):
        self.name = name
        self.age = age
        self.hair_color = hair_color
        self.ele_color = ele_color

    def dog_run(self):
        print(f"小狗{self.name},self是{self},正挥动它{self.legs_num}条小腿快速奔跑;")

    def dog_blink_eles(self):
        print(f"小狗{self.name},self是{self},正眨动它{self.eles_num}只{self.ele_color}的眼睛;")


Jamis = Dog('Jamis', 3, '黄色', '黑色')
Tom = Dog('Tom', 5, '黑色', '褐色')
Jamis.dog_run()  # self -> Jamis
Tom.dog_run() # self -> Tom

运行结果:
小狗Jamis,self是<__main__.Dog object at 0x00000151FF051F98>,正挥动它4条小腿快速奔跑;
小狗Tom,self是<__main__.Dog object at 0x00000151FF051FD0>,正挥动它4条小腿快速奔跑;

上面代码中Jamis对象的self指向的是Jamis对象,Tom对象的self指向的是Tom对象

(即:不同对象调用同一属性或方法,调用的是对象自己的属性和方法)

类属性与实例属性

类属性:类体中、所有方法之外:此范围定义的变量,称为类属性或类变量,例下面代码的eles_num与legs_num属性

class Dog:

    eles_num = 2  # 类属性,眼睛数量
    legs_num = 4  # 类属性,腿数量


    def __init__(self, name, age, hair_color, ele_color):
        self.name = name
        self.age = age
        self.hair_color = hair_color
        self.ele_color = ele_color

    def dog_run(self):
        print(f"小狗{self.name},self是{self},正挥动它{self.legs_num}条小腿快速奔跑;")

    def dog_blink_eles(self):
        print(f"小狗{self.name},self是{self},正眨动它{self.eles_num}只{self.ele_color}的眼睛;")

Jamis = Dog('Jamis', 3, '黄色', '黑色')
Tom = Dog('Tom', 5, '黑色', '褐色')

 

类属性的特点是,所有类的实例化对象都同时共享该变量,也就是说,类属性在所有实例化对象中是作为公用资源存在的,即以上述代码为例,eles_num与legs_num是实例对象Jamies与Tom所共有的,改变类中的变量值,会同时影响两个实例对象

类属性的访问

通过类名直接访问

Dog_eles_num = Dog.eles_num  # 通过类名Dog直接访问,类名.属性名
print(Dog_eles_num)

 通过实例对象访问

Jamis = Dog('Jamis', 3, '黄色', '黑色')
Dog_eles_num = Jamies.eles_num  # 通过实例对象名直接访问,实例对象.属性名
print(Dog_eles_num)

注意:通过”类名.属性名”的方式修改类属性,会影响所有对应的实例,反之”实例对象.属性名”方式修改类属性,只影响该实例对象,并不影响其他相同类创建的实例对象

实例属性:指的是在类的方法内部,以“self.变量名”的方式定义的变量,其特点是只作用于实例对象。另外,实例变量只能通过实例对象名访问,无法通过类名访问。

class Dog:

    eles_num = 2  # 类属性,眼睛数量
    legs_num = 4  # 类属性,腿数量


    def __init__(self, name, age, hair_color, ele_color):
        self.name = name  # 实例属性
        self.age = age
        self.hair_color = hair_color
        self.ele_color = ele_color

    def dog_run(self):
        print(f"小狗{self.name},self是{self},正挥动它{self.legs_num}条小腿快速奔跑;")

    def dog_blink_eles(self):
        print(f"小狗{self.name},self是{self},正眨动它{self.eles_num}只{self.ele_color}的眼睛;")

Jamis = Dog('Jamis', 3, '黄色', '黑色')
Tom = Dog('Tom', 5, '黑色', '褐色')

上述代码中self.name, self.age, self.hair_color, self.ele_color都是实例属性

实例属性的访问(只能通过实例对象访问)

Jamis = Dog('Jamis', 3, '黄色', '黑色')
Tom = Dog('Tom', 5, '黑色', '褐色')
Jamis_age = Jamis.age  # 调用Dog类实例化后,通过 实例对象.属性名 访问
Tom_color = Tom.hair_color

任务:创建一个Student类,包含name, age, country, gender, height实例属性,以及run( ), sleep( ), eat( ), study( )实例方法,并进行实例化后调用里面的属性,和方法;

任务:基于上一任务的实例对象,尝试修改这个student实例的age,并输出;

类中的方法

和类中的属性一样,类中的方法也可以进行更细致的划分,具体可分为类方法、实例方法和静态方法

类最基本的作用是通过实例化创建一个实例对象,通过实例对象才能访问实例方法,但是有的时候我们想不需要实例化直接访问类中的方法,而一个类中,某个方法前面加上了staticmethod或者classmethod的话,那么这个方法就可以不通过实例化直接调用,可以通过类名进行调用

类方法@classmethod

定义类方法基本语法如下

@classmethod
def 方法名(cls, [参数]):  # 类方法第一个参数必须是cls,这个跟self是一个作用,参数是自定义的形参
    方法内部代码块
class Car:

    brand_name = "大众"  # brand_name 就是类属性,类对象可直接调用

    def __init__(self, model, color):
        self.color = color

    def get_color(self):
        print(f"颜色是:{self.color}")

    def get_name(self):
        print(f"车的品牌名是:{Car.brand_name}")

    @classmethod
    def make(cls):
        print(f"{cls.brand_name}生产汽车")

if __name__ == '__main__':
	Car.make()

cls通常用作类方法的第一参数  类似实例方法中的self,cls传递当前类对象。

self 和cls 没有特别的含义,作用只是把实例对象或类对象绑定到方法上

类方法的调用

1、通过类名直接调用

Car.make()

 2、通过实例对象调用

vw_car = Car('甲壳虫', '蓝色')
vw_car.make()

静态方法@staticmethod

静态方法,其实就是我们学过的函数,和函数唯一的区别是,静态方法定义在类这个空间,而函数则定义在类外层的程序所在的空间中。

定义静态方法基本语法如下

 

@staticmethod
def 方法名([参数]):    # 静态方法不需要self, cls作为第一个参数
    方法内部代码块

 

class Car:

    brand_name = "大众"  # brand_name 就是类属性,类对象可直接调用

    def __init__(self, model, color):
        self.color = color

    def get_color(self):
        print(f"颜色是:{self.color}")

    def get_name(self):
        print(f"车的品牌名是:{Car.brand_name}")

    @classmethod
    def make(cls):
        print(f"{cls.brand_name}生产汽车",cls)

    @staticmethod
    def service():
        print("售前售后服务")

静态方法的调用与类方法调用方法基本一致

 

Car.service()    # 通过类名调用

vw_car = Car('甲壳虫', '蓝色')
vw_car.service()  # 通过实例对象调用

在实际编程中,几乎很少会用到类方法和静态方法,因为我们完全可以使用实例方法代替它们实现想要的功能,但某些功能不需要实例化,使用类方法和静态方法也是很不错的选择

私有变量

实例化一个类以后得到一个实例对象,可以通过“实例对象名.属性名”来访问这个实例的属性,同样可以通过“实例对象名.属性名 = xxx”来对这个属性进行修改,即外部代码可以通过这种方式修改实例属性。为了让程序健壮以及安全考虑,需要让内部属性不被外部所访问,因此可以在属性名称前加两个“”下划线,在定义类时,以“”开头的变量名称为私有变量,只能在类内部访问,外部无法访问

 

class Car:

    brand_name = "大众"  # brand_name 就是类属性,类对象可直接调用

    def __init__(self, model, color):
        self.__color = color  # 通过"__"来定义私有变量

    def get_color(self):
        print(f"颜色是:{self.__color}")

    def get_name(self):
        print(f"车的品牌名是:{Car.brand_name}")

    @classmethod
    def make(cls):
        print(f"{cls.brand_name}生产汽车",cls)

    @staticmethod
    def service():
        print("售前售后服务")

Car.service()
vw_car = Car('甲壳虫', '蓝色')
print(vw_car.__color)

 

上述代码,由于__color是私有变量,所以通过 vw_car.__color 外部访问,会提示没有这个对象,而通过 get_color 方法可访问 __color 完成输出;

注意,Python 类中还有以双下划线开头和结尾的类方法(例如类的构造函数__init__(self)),这些都是 Python 内部定义的,用于 Python 内部调用。我们自己定义类属性或者类方法时,不要使用这种格式。

任务:定义一个cat类,包含私有变量__name, __color,定义两个方法,get_color用于获取__color并通过return返回,set_name用于设置__name;

二、继承

继承机制经常用于创建和现有类功能类似的新类,又或是新类只需要在现有类基础上添加一些成员(属性和方法),但又不想直接将现有类代码复制给新类。也就是说,通过使用继承这种机制,可以轻松实现类的重复使用

继承语法如下

class 类名(父类1, 父类2, ...):
    #类定义部分

定义一个类时,可以通过继承一个现有的类,来拥有这个类的所有功能,而不需要重新额外定义,被继承的类称为父类

class Car:

    brand_name = "大众"  # brand_name 就是类属性,类对象可直接调用

    def __init__(self, model, color):
				self.model = model
        self.color = color  # 通过"__"来定义私有变量

    def get_color(self):
        print(f"颜色是:{self.color}")

    def get_name(self):
        print(f"车的品牌名是:{Car.brand_name}")

    @classmethod
    def make(cls):
        print(f"{cls.brand_name}生产汽车",cls)

    @staticmethod
    def service():
        print("售前售后服务")

class Limousine(Car):  # 定义一个轿车类,继承Car父类
    pass

class Truck(Car): # 定义一个卡车类,继承Car父类
    pass

limousine_car = Limousine('轿车', '蓝色')
limousine_car.get_color()  # limousine_car调用的是Car类中的get_color实例方法

truck_car = Truck('卡车', '白色')
truck_car.get_name()  # truck_car调用的是Car类中的get_name实例方法 

对于 limousine 来说,Car 就是父类,基类或超类,而 limousine 就是 Car 的子类

作为子类,可以增加自定义方法

class Limousine(Car):
    def auto_driver(self):
     	print("开始进行自动辅助驾驶")
limousine_car = Limousine('轿车', '蓝色')
limousine_car.auto_driver() # 开始进行自动辅助驾驶

上面代码中增加的 auto_driver 方法只存在于 limousine 中,对父类以及继承该父类的其他子类无影响 ;

对于子类继承的父类的方法,子类可以对继承的方法进行重写修改,这个子类运行调用该方法时,会运行修改后的方法

 

class Limousine(Car):

    def get_color(self):  # 此处是子类limousine对父类get_color重写
        print(f"型号是:{self.model}")
        print(f"颜色是:{self.color}")

class Truck(Car):

    def get_color(self):  # 此处是子类truck对父类get_color重写
        print(f"型号是:{self.model},颜色是:{self.color}")

limousine_car = Limousine('轿车', '蓝色')
limousine_car.get_color()  #型号是:轿车
													 #颜色是:蓝色

truck_car = Truck('卡车', '白色')
truck_car.get_color()     #型号是:卡车,颜色是:白色

 

当子类和父类都存在相同的get_color( )方法时,我们说,子类的get_color( )覆盖了父类的get_color( ),在代码运行的时候,总是会调用子类的get_color( )。

三、继承树

继承可以一层一层的进行继承,父类-子类-孙类,就好比人类的继承关系,爷-父-子-孙继承,但所有继承都可以向上追溯到一个根类 object,Python所有类都默认继承这个 object

注意,如果类没有显式指定继承自哪个类,则默认继承 object 类(object 类是 Python 中所有类的父类,即要么是直接父类,要么是间接父类)

class Car:
    pass

class Car(object):
    pass

上述代码,继承关系是一样的,默认继承 object 这个根类可不显示的写出来

class Car:
    pass

class Limousine(Car):
    pass

class Truck(Car):
    pass

再看一个继承实例

子类需要覆写构造方法

class Persion:

	rest = '休息日'
	work_study = '学习或工作日'

	def __init__(self,name,age,gender):
		self.name = name
		self.age = age
		self.gender = gender

	def eat(self):
		print(self.rest)
		print(f"{self.name} 正在吃饭")

	def drink(self):
		print(f"{self.name}正在喝水")

	def walk(self):
		print(f"{self.name} 正在走路")

	def sleep(self):
		print(f"{self.name} 正在睡觉")

    @classmethod
	def smile(cls):
		print("微微一笑")

	@staticmethod
	def hungry():
		print("肚子空空")


class Student(Persion):

    def __init__(self,name,age,gender,school,classname,number):
        super(Student).__init__(self,name,age,gender) # super(子类名)返回子类的父类
        # Persion.__init__(self,name,age,gender)
        self.school = school
        self.classname = classname
        self.number = number

    def sport(self):
        print(f"{self.name} 正在学校做运动")

    def study(self):
        print(f"{self.name}{self.age}岁,正在{self.school}{self.classname}上课")

if __name__ == "__main__":
   xiaoMing = Student(name='小明',age=12, gender='boy', school='兴华实验学校', classname='一(2)班',number='10')
   xiaoMing.study()
   xiaoMing.drink()

 

如果在子类中定义构造方法,则必须在该方法中调用父类的构造方法。

上述Student类继承了Person类,并对Person类的构造方法进行覆写

注:一定要用super(Student).init(self, name,age,gender)去调用父类的构造方法,否则,Student子类将没有name,age,gender三个属性,super(Student)将返回当前子类Student继承的父类,即Person

继承关系树如下

 Python基础之面向对象_第2张图片

小结:继承可以把父类的所有功能都直接拿过来,这样就不必重零做起,子类只需要新增自己特有的方法,也可以把父类不适合的方法覆盖重写。 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

你可能感兴趣的:(学习python,python)