Python 基础之面向对象(四)

什么是面向对象?

面向对象是一种编程范式,它将程序中的数据和操作数据的方法组合成对象,并通过对象之间的交互来实现程序的功能。在面向对象编程中,对象是程序的基本单元,每个对象都有自己的状态和行为。状态是对象的属性,行为是对象的方法。通过封装、继承和多态等特性,面向对象编程可以更好地管理和维护程序的复杂性,提高代码的可重用性和可维护性。

在Python中,一切皆为对象,包括数字、字符串、函数等,都是对象,因此Python是一种完全面向对象的编程语言。

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

  • 类:可以理解是一个模板,通过它可以创建出无数个具体实例。
  • 对象:类并不能直接使用,通过类创建出的实例(又称对象)才能使用。
  • 属性:类中的所有变量称为属性。
  • 方法:类中的所有函数通常称为方法。

1. 类

1.1 类的概念

一种数据类型就是类。例如整数,浮点数,字符串。

1.2 类的定义

Python 中通过关键字 class 可以定义一个自定义数据类型,基本语法如下:

class 类名:
	属性
	方法

注意:Python 中类名规则同变量,一般使用 大驼峰 表示;

案例

创建一个 Test 类用于表示测试中的一个点;

class Test:
    """
    表示测试中的一个点
    """
print(Test)

运行结果:  # 输出类本身

2. 对象

2.1 对象的概念

某种数据类型的一个具体的数据称为这个类的一个对象或者实例。通过类创建对象叫做实例化。

所谓的面向对象,就是把一些数据抽象成类的思想。

2.2 实例化

除了基本数据类型实例化的过程中用到的特殊的语法规范外,所有自定义类型进行实例化都是通过调用类名来实现的,非常简单。

语法如下:

类名(参数)

看起来和调用函数一样。

案例

给上面创建的 Test 类创建一个实例。

class Test:
    """
    表示测试中的一个点
    """
test1 = Test() # test1是Test类的对象或者实例;
print(test1)

运行结果: <__main__.Test object at 0x7fd01c1d4d50>

3. 属性

类和对象的特征数据称为属性。

3.1 类属性

类的属性是用来表明这个类是什么的。

类的属性分为实例属性类属性两种。

实例属性用于区分不同的实例;
类属性是每个实例的共有属性。

区别:实例属性每个实例都各自拥有,相互独立;而类属性有且只有一份,是共有的属性。

3.1.1 类属性的定义

直接在类中定义的变量(与class语句只有一个缩进),就是类属性。

案例:

给 Test 类创建一个 name 属性用来表示名称。

class Test:
    """
    表示测试中的一个点
    """
    name = '测试'

3.1.2 类属性的访问

类属性可以直接通过类名和对象以句点法访问。

语法格式如下:

类名.类属性名
对象.类属性名

案例

class Test:
    """
    表示测试中的一个点
    """
    name = '测试'
print(Test.name) # 使用类名.类属性名
运行结果: 测试

test1 = Test() # 设置对象名
print(test1.name) # 使用对象名.类属性名
运行结果: 测试



print(test1.name1) # 使用对象名.未知类属性名
注意:如果不存在属性则抛出AttributeError的异常

3.2 对象属性

对象的特征数据称为对象属性。

3.2.1 对象属性的定义

通过句点法对象.对象属性以赋值的方式可以直接定义对象属性。

案例:

测试中的每个点都有 x坐标 和 y坐标,通过类 Test 创建一个对象表示点(x=1,y=2)

class Test:
    """
    表示测试中的一个点
    """
    name = '测试'
test1 = Test() # 设置对象名
test1.x = 1 # 为对象设置对象属性 x
test1.y = 2 # 为对象设置对象属性 y

print(test1.x) # 使用 对象.对象属性名 x
运行结果: 1

print(test1.y) # 使用 对象.对象属性名 y
运行结果: 2

访问对象属性时,首先会检查对象是否拥有此属性,如果没有则去创建对象的类中查找有没有同名的类属性,如果有则返回,如果都找不到则抛出AttributeError的异常

4. 方法

定义在类中的函数称为方法。

通过调用的方式的不同,分为 对象方法、类方法、静态方法、魔术方法。

4.1 对象方法

定义在类中的普通方法,一般通过对象调用称为对象方法。

4.1.1 对象方法的定义

为了讲清楚对象方法的定义和调用,我们先看下面的案例。

案例:

定义函数 my_test, 它接收一个 Test 对象,然后打印这个点的 x,y坐标。

class Test:
    """
    表示测试中的一个点
    """
    name = '测试'
    def my_test(test1): # 这个是对象方法
        print('x坐标是{},y坐标是{}'.format(test1.x,test1.y))

4.1.2 对象方法的调用

对象方法向属性一样,可以通过句点法进行调用。

类名.方法名(参数)
对象.方法名(参数)

通过类名调用方法时,和普通函数没有区别;

因此,定义对象方法会习惯性的把第一个形参定义为self,表示调用对象本身;

class Test:
    """
    表示测试中的一个点
    """
    name = '测试'
    def my_test(self): # 第一个形参定义为self,表示调用对象本身;
        print('x坐标是{},y坐标是{}'.format(test1.x,test1.y))

test1 = Test() # 设置对象名

test1.x = 1 # 为对象设置对象属性 x
test1.y = 2 # 为对象设置对象属性 y

test1.my_test() # 使用 对象.对象属性名
Test.my_test(test1) # 使用 类名.对象方法名

4.2 类方法

在类中通过装饰器 @classmethod 可以把一个方法变成类方法。

一个类方法把类自己作为第一个实参,就像一个实例方法把实例自己作为第一个实参。

案例

定义一个类方法 base_test 用来返回坐标原点。

class Test:
    """
    表示测试中的一个点
    """
    name = '测试'

    def my_test(self):
        """
        对象方法
        :return:
        """
        print('x坐标是{},y坐标是{}'.format(test1.x,test1.y))

    @classmethod
    def base_test(cls):
        """
        类方法
        :return:
        """
        bt = cls()
        cls.x = 0
        cls.y = 0
        return bt


test1 = Test() # 设置对象名
test1.base_test().my_test() # 通过 对象.类方法名.对象方法 全链路调用
Test.base_test().my_test() # 通过 类名.类方法名.对象方法  全链路调用
运行结果: x坐标是0,y坐标是0


class Circle():
   pi = 3.14

   @classmethod
   def pi0(cls):
       return cls.pi

   def pi1(self):
       return self.pi

print(Circle.pi0())  # 没有实例化 能直接访问pi() 方法
print('--' * 10)
circle1 = Circle()
print(circle1.pi0()) # 也可以通过实例访问pi()方法
print('--' * 10)
print(circle1.pi1())

通过类本身或者是该类的实例都可以调用类方法。

注意:实例属性访问优先级比类属性高,所以我们访问时优先访问实例属性,它将屏蔽掉对类属性的访问。

4.3 特殊方法(魔术方法)

在类中可以定义一些特殊的方法用来实现特殊的功能,也称为魔术方法。

__init__ 又叫构造方法,初始化方法,在调用类名实例化对象时,构造方法会被调用,类名括号()后的参数会传递给构造方法,对象属性一般在这个方法中定义。

构造方法用于创建对象时使用,每当创建一个类的实例对象时,Python 解释器都会自动调用它。 语法格式如下:

def __init__(self,...):
    代码块
案例:

上面案例中的 Test 类实例化后,需要手动创建对象属性xy,这显然容易出错和不规范,正确的做法应该是在构造方法中定义属性xy;

class Test:
    """
    表示测试中的一个点
    """
    name = '测试'

    def __init__(self,x,y): # 初始化一个属性x,y(不要忘记self参数,他是类下面所有方法必须的参数)
        self.x = x
        self.y = y

    def my_test(self):
        """
        对象方法
        :return:
        """
        print('x坐标是{},y坐标是{}'.format(self.x,self.y))

    @classmethod
    def base_test(cls):
        """
        类方法
        :return:
        """
        bt = cls(0,0)
        return bt


test1 = Test(1,3) # 设置对象名
test1.my_test()
test2 = Test(x=3,y=1)
test2.my_test()
运行结果: 
x坐标是1,y坐标是3
x坐标是3,y坐标是1

面试喜欢问的问题:创建类时,类方法中的 self 是什么?
self 代表类的实例,是通过类创建的实例 (注意,在定义类时这个实例我们还没有创建,它表示的我们使用类时创建的那个实例)

流程图:

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

4.4 静态方法

在类中通过装饰器 @staticmethod 可以把一个方法变静态方法。

静态方法不会接收隐式的第一个参数,它和普通的函数一样,只是被封装到类中。

通过类和对象都可以调用。

案例:

在 Test 类中定义一个静态方法,用来计算两个数的和。

class Test:
    """
    表示测试中的一个点
    """
    name = '测试'

    def __init__(self,x,y):
        """
        初始化
        :param x: 
        :param y: 
        """
        self.x = x
        self.y = y

    def my_test(self):
        """
        对象方法
        :return:
        """
        print('x坐标是{},y坐标是{}'.format(self.x,self.y))

    @classmethod
    def base_test(cls):
        """
        类方法
        :return:
        """
        bt = cls(0,0)
        return bt

    @staticmethod
    def st_test(x,y):
        """
        静态方法
        :param x: 
        :param y: 
        :return: 
        """
        return x + y



print(Test.st_test(3,5)) # 通过 类.静态方法名调用
运行结果: 8

t = Test(1,4)
print(t.st_test(x=3,y=10)) # 通过 对象.静态方法名调用
运行结果: 13

4.5 构造方法、类方法、静态方法、普通方法 区别?

  1. 构造方法:构造方法是一种特殊的方法,它在创建类的实例对象时被调用。在Python中,构造方法是通过__init__()方法来实现的。它用于初始化对象的属性,通常会在创建对象时自动调用。

  2. 普通方法:普通方法是类中默认的方法类型,它需要一个实例化对象来调用,并且第一个参数必须是self,表示实例化对象本身。

  3. 类方法:类方法是通过@classmethod装饰器修饰的方法,它不需要实例化对象即可调用,并且第一个参数是cls,表示类本身。类方法可以访问类属性和类方法。

  4. 静态方法:静态方法是通过@staticmethod装饰器修饰的方法,它不需要实例化对象即可调用,也不需要cls参数,因为它既不能访问实例属性和方法,也不能访问类属性和方法。静态方法通常用于工具函数或者辅助函数的实现。

总结:构造方法是用于初始化对象的属性的特殊方法,普通方法需要实例化对象调用,类方法和静态方法都不需要实例化对象即可调用,类方法需要传入cls参数,可以访问类属性和类方法,静态方法不能访问实例属性和方法,也不能访问类属性和方法。

5. 类的继承

5.1 继承

当定义一个类时,可以从现有的类继承,新的类称为子类(Subclass),被继承的类称为基类,父类或超类(Base class,Super class).

子类可以继承父类的属性和方法。

什么时候使用继承:

假如需要定义几个类,而类与类之间有一些公共的属性和方法,这时我就可以把相同的属性和方法作为基类的成员,而特殊的方法及属性则在本类中定义。这样子类只需要继承基类(父类),子类就可以访问到基类(父类)的属性和方法了,它提高了代码的可扩展性和重用行。

案例:

创建一个类用来表示继承 Test 类。

class Test:
    """
    表示测试中的一个点
    """
    name = '测试'

    def __init__(self,x,y):
        """
        初始化
        :param x:
        :param y:
        """
        self.x = x
        self.y = y

    def my_test(self):
        """
        对象方法
        :return:
        """
        print('x坐标是{},y坐标是{}'.format(self.x,self.y))

    @classmethod
    def base_test(cls):
        """
        类方法
        :return:
        """
        bt = cls(0,0)
        return bt

    @staticmethod
    def st_test(x,y):
        """
        静态方法
        :param x:
        :param y:
        :return:
        """
        return x + y


class Test1(): 
    """
    未继承 Test 类
    """
print(dir(Test1))
运行结果: ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__']


class Test:
    """
    表示测试中的一个点
    """
    name = '测试'

    def __init__(self,x,y):
        """
        初始化
        :param x:
        :param y:
        """
        self.x = x
        self.y = y

    def my_test(self):
        """
        对象方法
        :return:
        """
        print('x坐标是{},y坐标是{}'.format(self.x,self.y))

    @classmethod
    def base_test(cls):
        """
        类方法
        :return:
        """
        bt = cls(0,0)
        return bt

    @staticmethod
    def st_test(x,y):
        """
        静态方法
        :param x:
        :param y:
        :return:
        """
        return x + y


class Test1(Test):
    """
    继承 Test 类
    """
print(dir(Test1))
Test.base_test()
t = Test(1,3)
br = t.base_test()
运行结果: ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'base_test', 'my_test', 'name', 'st_test']

在上面的案例中 Test1 类继承了 Test 类。

对于 Test1 来说 Test 是它的父类,对于 Test 类来说 Test1 是他的子类。

虽然在 Test1 类中没有定义任何的属性和方法,但它自动继承了父类 Test 的属性和方法。

Python 中子类与父类的关系:

class Test():
    pass

class Test1(Test):
    pass

T = Test()
T1 = Test1()

子类与父类的关系是 “is” 的关系,如上 Test1 继承于 Test 类,我们可以说:

“T”是 Test 类的实例,但“T”不是 Test1 类的实例。

“T1”是 Test 类的实例,“T1”也是 Test1 类的实例。

5.2 重写

        后续写

5.3 多态

        后续写

5.4 私有属性

        Python 类中如果有属性不希望被外部访问,我们可以在属性命名时以双下划线开头( __xxx__ ),那么该属性就不能使用原变量名访问,使得该属性变为本类私有的(伪私有)。

#错误实例
class Test:
    """
    表示测试中的一个点
    """
    name = '测试'
    def __base_test(self):
        """
        私有方法
        :return:
        """
        print('这是私有化方法')


Test.__base_test() #通过类.__base_test 访问报错
运行结果:
AttributeError: type object 'Test' has no attribute '__base_test'

#正确案例
class Test:
    """
    表示测试中的一个点
    """
    name = '测试'
    def __base_test(self):
        """
        私有方法
        :return:
        """
        print('这是私有化方法')
test = Test() # test是Test()的对象。
test._Test__base_test()
运行结果:
这是私有化方法
私有化方法后,我们只能在类的内部使用该方法,不能被外部调用。

同属性控制,方法需要访问被限制的方法也是 _类名__xx 如 test._Test__base_test。

6.自省与反射机制

6.1 自省

在计算机编程中,自省是指这种能力:检查对象以确定它是什么类型、它有哪些属性和哪些方法。自省向程序员提供了极大的灵活性和控制力。

isinstance

检查一个对象是否是某个或某些类型的实例

print(isinstance(1,int))
运行结果: True

print(isinstance('str',str))
运行结果: True

print(isinstance('str',list))
运行结果: False

issubclass

检查一个类是否是某个或某些类的子类

print(issubclass(bool,int))
运行结果: True

6.2 反射

反射就是动态的操作对象。

简单的讲就是根据字符串形式的属性名方法名操作对应的对象。

hasattr

检查一个对象是否有给定名称的属性

print(hasattr([1,2,3,4],'append'))
运行结果: True

print(hasattr({"set":'sex'},'pop'))
运行结果: True

getattr

返回一个对象给定名称的属性

import requests
print(getattr(requests,'post'))
运行结果: 

class Test():
    name = '测试'
print(getattr(Test,'name'))
运行结果:测试

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