Python——成员变量
前略:
1.pass关键字:
作用:
pass是占位语句,用来保证函数(方法)或类定义的完整性(如果不使用pass保证完整性,会导致程序报错),表示无内容,空的意思
一.类
1.类的定义和使用:
类的适用语法:
class 类名: # clas是关键字,表示要定义类了
类的属性 # 定义在类中的变量(成员变量)
类的行为 # 定义在类中的函数(成员方法)
创建类对象的语法:
对象名 = 类名()
在类中定义成员方法和定义函数基本一致,其中的差别最主要是在方法定义的参数列表中多出了一个self关键字
def 方法名(self, 形参1, ..., 形参n)
方法体
self关键字是在定义成员方法时必须填写的,它用来表示类对象自身
当我们使用类对象调用方法的时候self会自动被python传入
在方法的内部如果想要访问类的成员变量则必须使用self(如果在参数列表中没有加入self关键字,那么就会报错)
在传入参数时self是透明的,也就是说其实并没有参数会传入self中,我们所需要传入的参数的数量和形式参数的数量相等
二.类和对象
1.现实世界的事物和类
在最开始我们需要明确的是:现实世界的事物也有属性和行为,类也有属性和行为,而我们使用程序中的类,就可以完美地描述现实世界的事物
2.在前面我们提到了基于类来创建对象的语法是:对象名 = 类名()
那么我们为什么非要创建对象才能使用类呢?
举个例子,将类看作一个模板,我们使用模板来创建我们需要的事物,之后才能正常工作,这就是创建对象于类的意义
3.简单举例:
# 创建一个学生类以及两个测试对象和一个方法简单的来形容面向对象编程的运用
class student:
name = None
age = None
score = None
def study(self):
print(f"{self.name}同学正在学习,他的年龄是{self.age},这次考试的得分为:{self.score}")
student1 = student()
student1.name = "张三"
student1.age = 18
student1.score = 90
student1.study()
student2 = student()
student2.name = "李四"
student2.age = 18
student2.score = 80
student2.study()
三.构造方法;
在python类中可以使用__init__()方法(即构造方法)完成传递传入参数到对象中的任务
1.简单举例:
class student:
name = None
age = None
score = None
def __init__(self, name, age, score):
self.name = name
self.age = age
self.score = score
def study(self):
print(f"{self.name}同学正在学习,他的年龄是{self.age},这次考试的得分为:{self.score}")
student1 = student("张三", 18, 90)
student1.study()
student2 = student("李四", 18, 80)
student2.study()
使用构造方法就能大量省略传递参数的这一步骤
甚至我们还能省略最初的形参,使代码更加简洁
class student:
# 这三行初始化可写可不写
# name = None
# age = None
# score = None
def __init__(self, name, age, score):
self.name = name
self.age = age
self.score = score
def study(self):
print(f"{self.name}同学正在学习,他的年龄是{self.age},这次考试的得分为:{self.score}")
student1 = student("张三", 18, 90)
student1.study()
student2 = student("李四", 18, 80)
student2.study()
注意:构造方法也是成员方法的一种,我们需要在参数列表中加入self这个参数,而且我们在构造方法内定义成员变量时需要使用self关键字(将传入参数传到形参中)
四.魔术方法:
我们一般称Python类内置的方法(如__init__()方法)为魔术方法,因为它们各自有特殊的功能
1.__str__()
当类对象需要被转换为字符串时会输出一个内存地址
class student:
def __init__(self, name, age, score):
self.name = name
self.age = age
self.score = score
student1 = student("张三", 18, 90)
print(student1)
student2 = student("李四", 18, 80)
print(student2)
比如这段代码最后输出的结果就是这样的:
如果我们想要转换成我们所需要的文字内容(字符串类型),那么就需要进行一点修改
class student:
def __init__(self, name, age, score):
self.name = name
self.age = age
self.score = score
def __str__(self):
return f"该同学的名字为:{self.name},年龄为:{self.age},本次考试的得分为:{self.score}"
# 下方这两种形式皆可
student1 = student("张三", 18, 90)
print(student1)
student2 = student("李四", 18, 80)
print(str(student2))
像这样就能够显示出我们所需要的文字内容了
2.__lt__()
在python中,直接对两个对象进行比较是不可以的,会报错(因为程序不知道你的比较基准是什么),但是只要在类中实现__lt__()方法就可以完成两个对象之间的小于或大于的比较
class student:
def __init__(self, name, age, score):
self.name = name
self.age = age
self.score = score
# other这个参数表示的是另一个对象
def __lt__(self, other):
return self.score < other.score
student1 = student("张三", 18, 90)
student2 = student("李四", 18, 80)
print(student1 < student2)
最后的结果则是一个布尔类型的值
3.__le__()
在上方的__lt__()方法中,大家可能注意到了:那么我怎么比较等于的情况呢?__le__()方法则可以用于大于等于或小于等于这两种运算上
class student:
def __init__(self, name, age, score):
self.name = name
self.age = age
self.score = score
# other这个参数表示的是另一个对象
def __le__(self, other):
return self.score <= other.score
student1 = student("张三", 18, 90)
student2 = student("李四", 18, 80)
print(student1 <= student2)
4.__eq__()
这就是纯粹的等于的比较了,跟上方两个方法差不多,就不怎么介绍了
class student:
def __init__(self, name, age, score):
self.name = name
self.age = age
self.score = score
# other这个参数表示的是另一个对象
def __eq__(self, other):
return self.score == other.score
student1 = student("张三", 18, 90)
student2 = student("李四", 18, 80)
print(student1 == student2)
注意如果不写这个方法,那么就会比较两个对象的内存地址,只要是两个不同的对象,那么内存地址就一定不同,所以返回结果一定会是False
五.面向对象
1.面向对象的简答介绍:
面向对象编程是许多编程语言都支持的一种编程思想(Java,python等)
对于面向对象的简单理解是:基于模板(类)去创建实体(对象),然后使用对象完成功能开发
2.面向对象的三大特性:
(1).封装
封装表示的是:将现实世界事物的属性和行为封装到类中,描述为:成员变量和成员方法,从而完成程序对现实世界事物的描述
属性——成员变量
行为——成员方法
对用户隐藏的属性和行为:
在现实世界中的事物都有属性和行为,但是在我们以程序对它们进行描述时并不会将这些所有的事物,属性和行为都开放给用户使用(仅限权限用户使用)
所以在程序中作为映射的类也应该支持这种思想,因此诞生的就是私有成员
①:私有成员:
类中提供了私有成员的形式来支持,所以其中的内容为:私有成员变量和私有成员方法
定义私有成员的方法也非常简单,只需要在成员变量或成员方法之前以两个下划线(__)开头即可
class student:
# 私有成员变量
__rank = None
def __init__(self, name, age, score):
self.name = name
self.age = age
self.score = score
# 私有成员方法
def __print_rank(self):
print(f"该同学的年级排位为:{self.__rank}")
②:使用私有成员
在我们定义了私有成员之后,私有方法是无法直接被类对象使用的,私有变量也是无法赋值,并且无法获取值的(一旦想要这样直接使用就会报错——AttributerError)
那么我们该怎么使用私有成员呢?
事实上,私有成员无法被类对象使用,但是可以被其它的成员使用(私有的只有在内部自己使用,不给外人使用)
class student:
# 私有成员变量
# 测试私有变量的其他值,只需要修改__rank即可
__rank = 1
def __init__(self, name, age, score):
self.name = name
self.age = age
self.score = score
# 私有成员方法
def __print_rank(self):
print("该同学不是第一名")
# 重新定义一个方法使用私有成员
def rank(self):
if self.__rank == 1:
print("该同学是第一名")
else:
self.__print_rank()
stu = student("张三", 18, 100)
stu.rank()
(2).继承
继承的含义:
从父类那里继承(复制)所有的成员变量和成员方法(不包含私有的)
继承的写法:
class 类名(父类名):
类内容体
继承可分为单继承和多继承
①:单继承
# 单继承
class student:
def __init__(self, name, age, score):
self.name = name
self.age = age
self.score = score
def rank(self):
print(f"该同学姓名为:{self.name},成绩为{self.score}")
class student_class1(student):
def getage(self):
print(f"该同学年龄为:{self.age}")
student1 = student_class1("张三", 18, 100)
student1.rank()
②:多继承
Python的类之间也支持多继承,即一个类可以继承多个父类,写法为:
class 类名(父类1, 父类2, ..., 父类n)
类内容体
多继承简单实例:
# 多继承
class student:
def __init__(self, name, age, score):
self.name = name
self.age = age
self.score = score
def rank(self):
print(f"该同学姓名为:{self.name},成绩为{self.score}")
class student_class1:
def getage(self):
print(f"该同学年龄为:{self.age}")
class student_class2(student, student_class1):
def study(self):
print(f"{self.name}同学正在学习")
student2 = student_class2("张三", 18, 100)
student2.rank()
student2.getage()
student2.study()
注意:在多继承中,如果有同名的成员(成员变量,成员方法),那么默认以继承顺序(从左到右)为优先级(先继承的保留,后继承的被覆盖,即:讲究先来后到)
③:复写
含义:子类在继承父类的成员属性和成员方法后,如果想要对其进行修改以满足自身的需求,那么就可以进行复写
定义方法:
在子类中重新定义同名的属性或方法即可
# 复写
class student:
def __init__(self, name, age, score):
self.name = name
self.age = age
self.score = score
def study(self):
print(f"{self.name}同学预计学习六个小时")
class student_class1(student):
def study(self):
print(f"{self.name}同学预计从早上八点开始学习,每学习三个小时休息半小时")
student = student_class1("张三", 18, 100)
# 这里的study调用的是student_class1中复写的study方法
student.study()
④:调用父类的同名成员
一旦复写了父类成员,那么类对象调用成员的时候就会调用复写后的成员
那么如果我们需要使用复写之前的父类的成员,就需要使用到特殊的调用方式:
super:
super().成员(成员变量或成员方法)
使用super调用父类方法的简单实例:
# 在复写后调用复写前的父类中的成员
class student:
def __init__(self, name, age, score):
self.name = name
self.age = age
self.score = score
def study(self):
print(f"{self.name}同学预计学习六个小时")
class student_class1(student):
def study(self):
super().study()
print(f"{self.name}同学预计从早上八点开始学习,每学习三个小时休息半小时")
student = student_class1("张三", 18, 100)
# 这里的study调用的是student_class1中复写的study方法,但是我们在子类的study方法中还使用了super方式调用了父类中的study方法
student.study()
(3).多态
多态指的是多种状态,即完成某个行为时使用不同的对象会得到不同的状态
多态的运行原理:
①:以父类做定义声明
②:以子类做实际工作
③:用以获得同一行为,不同状态
多态的简单案例:
class Animal:
def voice(self):
print("发出声音")
class Dog(Animal):
def voice(self):
print("汪")
class Cat(Animal):
def voice(self):
print("喵")
def make_voice(animal: Animal):
animal.voice()
dog = Dog()
cat = Cat()
make_voice(dog)
make_voice(cat)
多态在抽象类中的用法:
在父类,如上方的Animal中的speak方法可以是空实现的(pass),像这样设计的含义是:
①:父类用来确定有哪些方法
②:具体的方法实现是由子类来自行决定的,而像这样的写法就叫做抽象类(接口)
抽象类:
含有抽象方法的类被称为抽象类
抽象方法:
方法体是空实现(pass)的方法
抽象类的作用:抽象类的作用是定义一个标准,其中包含的抽象方法要求子类以自己的方式进行实现
因此抽象类可以配合多态完成具体的项目需求:
①:抽象的父类设计(设计标准)
②:具体的子类实现(实现标准)
简单的抽象类配合多态实例:
class Animal:
def voice(self):
pass
def eat(self):
pass
def relax(self):
pass
class Dog(Animal):
def voice(self):
print("汪")
def eat(self):
print("吃肉")
def relax(self):
print("玩飞盘")
class Cat(Animal):
def voice(self):
print("喵")
def eat(self):
print("吃鱼")
def relax(self):
print("睡觉")
def animals_activities(animal: Animal):
animal.voice()
animal.relax()
animal.eat()
dog = Dog()
cat = Cat()
animals_activities(dog)
animals_activities(cat)
3.类型注解
为什么我们需要使用类型注解?
在我们使用Pycharm等编译软件时,自动补全功能以及提示功能给我们带来了很多的便利,但是如果我们对数据的类型没有进行注解,使得编译器无法知晓当前的数据到底是什么类型时,就不会出现这样的提示,所以我们需要使用到类型注解
类型注解的含义:
在代码中涉及数据交互的地方,提供数据类型的注解(显式的说明)
类型注解的主要功能:
(1).帮助第三方工具对代码进行类型推断,协助做代码提示
(2).帮助开发者自身对变量进行类型注释
类型注解所支持的类型:
(1).变量的类型注解
# 基础数据类型注解
var_1: int = 10
var_2: float = 3.14
var_3: bool = True
var_4: str = "this is a string"
(2).函数(方法)的形参列表和返回值的类型注解
# 类对象类型注解
class student:
pass
# 创建的stu这个对象的数据类型为student
stu: student = student()
(3).数据容器类型注解:
# 基础数据容器类型注解
my_list: list = [1, 2, 3]
my_tuple: tuple = (1, 2, 3)
my_set: set = {1, 2, 3}
my_dict: dict = {"dict": 1}
my_str: str = "str"
如果还要对数据容器内的数据的数据类型再进一步细化的话也是可以做到的
# 基础数据容器类型注解进一步细化
my_list: list[int] = [1, 2, 3]
my_tuple: tuple[int, int, int] = (1, 2, 3)
my_set: set[int] = {1, 2, 3}
my_dict: dict[str, int] = {"dict": 1}
(4).使用注释的注解方式:
除了使用上方的方式进行注解,还可以使用注释的方式进行注解,格式为:
# type:类型
简单示例:
class student:
pass
var1 = 10 # type: int
var2 = student() # type: student
注解的使用场景:在无法直接看出变量类型时我们会添加上变量的类型注解
注意:就算注解类型标记错了也不会影响程序的运行(但是还是要注意尽量不要写错,避免带来误会)
(5).函数中的类型注解:
①:对于函数中的形参进行类型注解的格式:
def 函数方法名(形参名: 类型, 形参名: 类型, ..., 形参名:类型):
方法体
简单的案例:
def add(x: int, y: int):
return x + y
②:对于函数中的返回值进行注解的格式:
def 函数方法名(形参: 类型, 形参, 类型, ..., 形参, 类型) -> 返回值类型:
方法体
简单案例:
def function(data: list) -> list:
return data
注意:在函数中进行类型注解,同样只是起到提示作用,并不会左右程序的运行,但我们最好还是为了避免引起误会而将它们写正确
(6).Union类型注解
在使用之前记得导入union包
from typing import union
之后我们就可以使用Union[类型1, 类型2, ..., 类型n]的形式定义联合类型注解了
简单案例:
# 如果使用先前的注解方式一个一个对应就会很麻烦,直接使用union联合类型注解说明这个列表里面的数据类型包含int和str两种即可
my_list: list[union[str, int]] = [1, "list", 2, "tuple"]
注意:union联合类型注解同样可以同时适用于参数和类两种类型的数据注解中