python从入门到精通:面向对象

发布于:2024-09-18 ⋅ 阅读:(54) ⋅ 点赞:(0)

目录

1、初识对象

使用对象数据组织:

2、成员方法

类的定义和使用语法

成员方法的使用

3、类和对象

4、构造方法

5、其他内置方法

__str__字符串方法:

__lt__小于符号比较方法:

__len__小于等于比较符号方法:

__eq__比较运算符实现方法: 

6、封装

私有成员:

 使用私有成员:

7、继承

继承的基本语法:

复写和使用父类成员:

8、类型注解

变量的类型注解:

函数(方法)的类型注解:

Union类型:

9、多态



1、初识对象

在程序中简单使用变量来记录学生信息:

student_1 = {"姓名":"小李","性别":"男"}
student_2 = "我叫小刘,今年18,男性"
student_3 = [
    "小杜",
    "20",
    "男"
]

但使用变量记录数据太乱了,数据组织就会方便不少。

使用对象数据组织:

在程序中是可以做到和生活中那样,设计表格、生产表格、填写表格的组织形式的。

1、在程序中设计表格,我们称之为设计类(class)

class Student:
    name = None # 记录学生姓名

 2、在程序中打印生产表格,我们称之为创建对象

# 基于类创建对象
stu_1 = Student()
stu_2 = Student()

3、在程序中填写表格,我们称之为对象属性赋值 

stu_1.name = "小李" # 为学生1对象赋予名称属性值
stu_2.name = "小张" # 为学生2对象赋予名称属性值

2、成员方法

类的定义和使用语法

类的使用语法:

class 类名称:class是关键字,表示要定义类了

类的属性:类的属性,即定义在类中的变量(成员变量)

类的行为:类的行为,即定义在类中的函数(成员方法)

创建类对象的语法:

对象=类名称()  

成员方法的使用

class Student:
    name = None # 记录学生姓名
    age = None # 记录学年龄

    def say_hi(self):
        print(f"大家好!,我是{self.name}")
student1 = Student()
student1.say_hi()

可以看出,类中:不仅可以定义属性来记录数据,也可以定义函数,用来记录行为。

其中:类的定义属性(变量),我们称之为成员变量;类中的行为(函数),我们称之为成员方法。 

在类中定义成员方法和定义函数基本一致,但仍有细微区别:

def 方法名(self,形参1,......,形参N):

        方法体 

可以看到,在方法定义的参数列表中,有一个:self关键字

self关键字是成员方法定义的时候,必须填写的。

· 它用来表示类对象自身的意思

· 当我们使用类对象调用方法时,self会被自动被python传入

· 在方法内部,想要访问类的成员变量,必须使用self 

注意事项:

class Student:
    name = None
    def say_hi(self):
        print('Hello World!')
    def say_hello(self,msg):
        print(msg)
stu = Student()
stu.say_hi() #调用时无需传参
stu.say_hello("哈哈") # 调用的时候,需要穿msg参数

 因此在传参的时候,self是透明的,可以不必理会。

3、类和对象

现实世界的事物也有属性和行为,类也有属性和行为。使用程序中的类,可有完美地描述现实世界的事物。

之所以创建对象:类只是一种程序内的”设计图纸“,需要基于图纸生产实体(对象),才能正常工作。这种思想,称之为:面向对象编程。

class Clock:
    id = None
    price = None
    def ring(self):
        import winsound
        winsound.Beep(2000,2000)
# 构建2个闹钟对象并使其工作
clock1 = Clock()
clock1.id = "0001"
clock1.price = 100
print(f"闹钟ID:{clock1.id},闹钟价格:{clock1.price}")
clock1.ring()
clock2 = Clock()
clock2.id = "0002"
clock2.price = 50
print(f"闹钟ID:{clock1.id},闹钟价格:{clock1.price}")
clock2.ring()

4、构造方法

python类可以使用:__init__()方法,称之为构造方法。

可以实现:

· 在创建类对象(构造类)的时候,会自动执行.

· 在创建类对象(构造类)的时候,将会传入参数自动传递给__init__方法使用 

# 构造对象
class Student:
    name = None # 可以省略
    age = None # 可以省略
    tel = None # 可以省略
    def __init__(self, name, age, tel):
        self.name = name
        self.age = age
        self.tel = tel
        print("Student类创建了一个对象")
stu = Student("张三",30,"17702119333")

 开学了有一批学生信息需要录入系统,请设计一个类,记录学生的:
姓名、年龄、地址,这3类信息
请实现:
· 通过for循环,配合input输入语句,并使用构造方法,完成学生信息的键盘录入
· 输入完成后,使用print语句,完成信息的输出

class Student:
    def __init__(self, name, age, address):
        self.name = name
        self.age = age
        self.address = address
num_student = 10
for i in range(num_student):
    name = input("请输入学生姓名:")
    age = input("请输入学生年龄:")
    address = input("请输入学生地址:")
    student = Student(name, age, address)
    print(f"第{i+1}个学生姓名:{student.name},学生年龄:{student.age},学生地址{student.address}")

5、其他内置方法

如__init__构造方法,使python类内置的方法之一。这些内置的类方法,各自有各自特殊的功能,这些内置的方法我们称之为:魔术方法。

__str__字符串方法:

class Student:
    def __init__(self, name, age):
        self.name = name
        self.age = age
student = Student("周周",21)
print(student) #结果:<__main__.Student object at 0x000001A95300EAB0>
print(str(student)) #结果:<__main__.Student object at 0x000001A95300EAB0>

当类对象需要被转换为字符串时,会输出如上结果(内存地址),我们可以通过__str__方法,控制类转换为字符串行为。

class Student:
    def __init__(self, name, age, ):
        self.name = name
        self.age = age
    def __str__(self):
        return f"student name is {self.name}, age is {self.age}"
student = Student("周周",23)
print(student) #运行结果:student name is 周周, age is 23
print(str(student)) #运行结果:student name is 周周, age is 23

__lt__小于符号比较方法:

#__lt__小于符号比较方法
class Student:
    def __init__(self, name, age):
        self.name = name
        self.age = age
stu1 = Student('zhang', 18)
stu2 = Student('zhang', 19)
print(stu1 < stu2) #TypeError: '<' not supported between instances of 'Student' and 'Student'

 直接两个对两个对象进行比较是不可以的,但是在类中实现__lt__方法,即可同时完成:小于符号和大于符号两种比较。

class Student:
    def __init__(self, name, age):
        self.name = name
        self.age = age
    def __lt__(self, other):
        return self.age < other.age # 当前年龄与被比较参数的年龄进行比较
stu1 = Student('zhang', 18)
stu2 = Student('li', 19)
print(stu1 < stu2) # 结果为true
print(stu1 > stu2) # 结果为false

__len__小于等于比较符号方法:

class Student:
    def __init__(self, name, age):
        self.name = name
        self.age = age
    def __le__(self, other):
        return self.age <= other.age # 当前年龄与被比较参数的年龄进行比较
stu1 = Student('zhang', 18)
stu2 = Student('li', 19)
print(stu1 <= stu2) # 结果为true
print(stu1 >= stu2) # 结果为false

__eq__比较运算符实现方法: 

# __eq__,比较运算符实现方法
class Student:
    def __init__(self, name, age):
        self.name = name
        self.age = age
    def __eq__(self, other):
        return self.age == other.age
stu1 = Student('zhang', 18)
stu2 = Student('lisi', 18)
print(stu1 == stu2) # 结果为true

需要注意的是:

1、不使用__eq__比较运算符,对象之间也可以进行比较,但是是比较的地址。也即是:不同对象==比较一定是False的结果

2、使用了__eq__方法,就可以按照自己的想法来决定两个值是否相等了 

6、封装

面向对象编程,是许多编程语言都支持的一种编程思想。简单理解就是:基于模板(类)去创建实体(对象),使用对象完成功能开发。

封装表示的是将现实世界的属性和行为封装到类中,描述为成员变量和成员方法。从而完成程序对现实世界事物的描述。

现实世界中的事物,有属性和行为。但并不代表着这些属性和行为都是开放给用户的。

私有成员:

既然现实事物有不公开的属性和行为,那么作为现实事物在程序中映射的类,也应该支持。

类中提供了私有成员的形式来支持:

· 私有成员变量

· 私有成员方法

私有成员的定义方式非常简单,只需要:

· 私有成员变量:变量名__开头 

· 私有成员方法:方法名以__开头

举个例子:

class  Phone:
    IMEI = None # 序列号
    producer = None # 厂商
    __current_voltage = None #当前电压(私有变量)

    def call_by_5g(self):
        print("5g已启用")

    def __keep_single_core(self): # 私有成员方法
        print("让CPU以单核模式进行以节省电量")
phone = Phone() #创建对象
phone.__keep_single_core() # 使用私有成员方法(报错)
phone.__current_voltage = 5.0 # 不报错,但无效
print(phone.__current_voltage) # 报错,无法使用

 使用私有成员:

私有成员无法被类对象使用,但是可以被其他的成员使用。

class  Phone:
    __current_voltage = 5 #当前电压(私有变量)
    def __keep_single_core(self): # 私有成员方法
        print("让CPU以单核模式进行以节省电量")
    def call_by_5g(self):
        if self.__current_voltage >= 1
            print("5g已启用")
        else:
            self.__keep_single_core()
            print("电量不足,不能启动5G,以自动启动单核节省电量")

实际意义:在类中提供仅内部使用的属性方法方法,而不对外开放(类对象无法使用),起到保护数据的作用,保护代码隐私,设置权限,防止某些属性修改。 

7、继承

继承就是指把已有的东西(类)拿过来进行修改,达到从新进行使用的目的。

比如:

class Phone:
    IMEI = None
    prodecer = None
    def call_by_4g(self):
        print("4g通话")
class Phone2022(Phone):
    face_id = True # 支持面部识别
    def call_by_5g(self):
        print("2022最新5g")

继承的基本语法:

class 类名(父类名):

        类内容体

继承分为:单继承和多继承。比如上一个例子就是一个典型的单继承 

继承:将从父类那里继承(复制)来成员变量和成员方法(不含私有)

多继承:python的类之间也支持多继承,即一个类,可以继承多个父类。语法:

class 类名(父类1,父类2,...,父类N):

        类内容体

class Phone:
    IMEI = None
    prodecer = None
    def call_by_5g(self):
        print("5g通话")
class NFCReader:
    nfc_type = "第五代"
    producer = "小米"
    def read_card(self):
        print("读取NFC卡")
    def wrirte_card(self):
        print("写入NFC卡")
class RemoteControl:
    rc_type = "红外遥控"
    def control(self):
        print("开启红外遥控")
class MyPhone(Phone, NFCReader, RemoteControl,):
    pass # 用来补全语法

注意事项: 多个父类中,如果有同名的成员,那么默认以继承顺序(从左到右)为优先级。即:先继承的被保留,后继承的被覆盖。

复写和使用父类成员:

复写:子类继承父类的成员属性和成员方法后,如果对其“不满意”,可以进行复写。即:在子类中重新定义同名的属性或方法即可。

class Phone:
    IMEI = None
    producer = "apple"
    def call_by_5g(self):
        print("父类5g通话")
class MyPhone(Phone):
    producer = "小米"
    def call_by_5g(self):
        print("子类的5g通话")

调用父类同名成员:一旦复写父类成员,那么类对象调用成员的时候,就会调用复写后的新成员,如果需要使用被复写的父类的成员,需要特殊的调用方式。

方式1:

· 调用父类成员

        使用成员变量:父类名.成员变量

        使用成员方法:父类名.成员方法(self)

方式2:

 · 使用super()调用成员父类

        使用成员变量:super().成员变量

        使用成员方法:super().成员方法()

class Phone:
    IMEI = None
    producer = "apple"
    def call_by_5g(self):
        print("开启5g通话")
class MyPhone(Phone):
    producer = "小米"
    def call_by_5g(self):
        print("开启CPU单核模式,确保通话时省电")
        # 方式1:
        Phone.call_by_5g(self)
        # 方式2:
        super().call_by_5g()
        print("关闭CPU单核模式,确保性能")

8、类型注解

我们自定义的模块或者函数等,pycharm是无法通过代码确定应传入什么类型,我们需要使用类型注解。

python在3.5版本之后引入了类型注解,一边方便静态类型检查工具,IDE等第三方工具。

类型注解:在代码中设计数据交互的地方,提供数据类型的注解(显示的说明)。

主要功能:

· 帮助第三方IDE工具(比如pycharm)对代码进行类型推断,协助做代码提示

· 帮助开发者自身对变量进行类型注解。

支持:

· 变量的函数注解

· 函数(方法)形参列表和返回值的类型注解 

变量的类型注解:

基础语法:变量:类型 

var_1:int=10
var_2:float = 3.14
var_3:str = 'python'
# 类对象类型注解
class Student:
    pass
stu: Student = Student()
# 数据容器类型注解
my_list:list = [1,2,3]
my_dict:dict = {"python":666}
my_set:set = {1,2,3}
my_tuple:tuple = (1,2,3)
# 数据容器类型详细注解
my_list:list[int] = [1,2,3]
my_dict:dict[str,int] = {"python":666}

 注意:元组类型设置详细注解,需要将每个元素都标记出来;字典类型设置类型详细注解,需要两个类型,第一个是key第二个是value。

除了使用变量:类型,这种语法做注解外,也可以在注解中进行类型注解。语法:

# type:类型

class Student:
    pass
var_1 = random.randint(1,10) # type: int
var_2 = json.loads(data) # type: dict[str,int]
var_3 = func() # type: Student

一般,在我们无法看出变量类型之时才会添加变量的类型注解。

class Student:
    pass
var_1: int = random.randomint(1,10)
var_2: dict = json.loads(data)
var_3: Student = Student()

 类型注解的限制在于不会对类型做检验和判断。也就是,类型注解仅仅是提示性的,不是决定性的。比如下方代码是不会报错的:

var_1: int = "python"

var_2: str = 123 

函数(方法)的类型注解:

函数和方法的形参类型注解语法:

def 函数方法名(函数名:类型,形参名:类型,......):

        pass

返回值注解:

def 函数方法名(形参:类型,...,形参:类型) -> 返回值类型:

        pass 

def func(data:list) -> list:
    return data

Union类型:

from typing import Union
my_list:list[Union[str,int]] = [1,2,"python"]
my_dict:dict[str, Union[str, int]] = {"name":"李白","age":23}

使用Union[类型,...,类型],可以定义联合类型注解,在变量注解、函数(方法)形参和返回值注解中,均可使用。 

9、多态

多态指的是多种状态,即完成某个行为时,使用不同的对象得到不同的状态。

多态常作用在继承关系上。比如:

1、函数(方法)形参声明接受父类对象

2、时机传入的子类对象进行工作

即:

1、以父类做定义声明

2、以子类做实际工作

3、用以获得同一行为、不同状态 

class animals:
    def speak(self):
        pass
class dog(animals):
    def speak(self):
        print("汪汪")
class cat(animals):
    def speak(self):
        print("喵喵")
def make_noise(animals: animals):
    animals.speak()
dog = dog()
cat = cat()
make_noise(dog)
make_noise(cat)

 抽象类(接口)

上述例子中animals的speak方法是空实现,这种设计的含义:

1、父类用来确定有哪些方法

2、具体的方法实现,由子类自行决定

这种写法就叫做抽象类(也可以称之为接口)

抽象类:含有抽象方法得类统称抽象类

抽象方法:方法是空实现的(pass)称之为抽象方法

可以配合多态,完成抽象父类的实现(设计标准)以及具体子类的实现(实现标准)