学习 Python 之 面向对象

Posted _DiMinisH

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了学习 Python 之 面向对象相关的知识,希望对你有一定的参考价值。

学习 Python 之 面向对象

python面向对象

类与对象, 类的最基本作用是封装

类的操作如何操作
类的定义使用class关键字
类的实例化变量名 = 类名(参数)
类函数的定义使用classmethod装饰器, 第一个参数代表该类
实例函数的定义直接在类中定义函数, 第一个参数代表该对象
静态函数使用staticmethod装饰器

1. 类的定义

使用class关键字定义一个类

类中可以定义变量, 定义函数

学生类

class Student:
    name = ''
    age = 0
    
    def fun(self):
        pass

2. 类的实例化

class Student:
    name = ''
    age = 0
    
    def fun(self):
        pass
    
student = Student()

3. 调用类中的实例方法

类中实例方法的一个参数代表对象本身, 它用来访问对象中的实例变量

class Student:
    name = ''
    age = 0
    
    def fun(self):
        print(self.name, self.age)
    
student = Student()
student.fun()

4. 使用实例变量

使用.运算符

class Student:

    def fun(self):
        print(self.name, self.age)
    
student = Student()
print(student.name, student.age)

5. 类与对象

类是现实世界或思维世界中的实体在计算机中的反映, 它将数据以及这些数据上的操作封装在一起

类中的函数和变量称为数据成员

类的特征由成员变量表示, 类的行为由成员函数表示

6. 类的构造函数

构造函数用来初始化类中的变量, 构造函数的名字必须是__init__

def __init__(self):
        pass

当对象实例化时, 会自动调用构造函数, 不需要显式的调用

class Student:
    name = ''
    age = 0

    def fun(self):
        print(self.name, self.age)

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

student = Student('a', 10)
student.fun()

a 10

注意:

  1. 构造函数只能返回None
  2. 构造函数可以显式调用, 但是返回值是None
  3. 定义了构造方法后, 如果构造方法需要传入参数, 创建对象时必须传入参数

7. 类变量与实例变量

(1). 理解类变量与实例变量

类变量: 与类相关的变量

实例变量: 与对象相关的变量, 即对象可以访问的变量

简单理解: 类中直接定义的变量是类变量, 使用self.xxx定义的变量是实例变量

学生类中, xy是类变量
self.age, self.name是实例变量

class Student:
    x = ''
    y = 0

    def fun(self):
        print(self.name, self.age)

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

下面给实例变量赋值

student1 = Student('a', 10)
student2 = Student('b', 10)

注意打印结果

class Student:
    x = ''
    y = 0

    def fun(self):
        print(self.name, self.age)

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



student = Student('a', 10)
print(student.name)
print(Student.name)
a
AttributeError: type object 'Student' has no attribute 'name'

第一个输出的是对象的实例变量的值

第二个应该输出的是类的变量的值, 但是类没有该变量, 所以抛出异常了

类只能调用类变量, 实例可以调用实例变量和类变量

(2). 类变量和实例变量的查找顺序

使用内置属性__mro__可以查看查找顺序

无继承

对于一个对象

class A:
    pass
a = A()
print(a.x)

查找x的顺序是: 实例变量 -> 查找类变量, 都没有就报错

单继承

查找的顺序是: 该类实例变量 -> 该类类变量 -> 父类实例变量 -> 父类的类变量, 都没有就报错

class A:
    x = 'A的类变量'
    def __init__(self):
        # self.x = 'A的实例变量'
        pass

class B(A):
    # x = 'B的类变量'
    def __init__(self):
        super().__init__()
        # self.x = 'B的实例变量'
b = B()
print(b.x)

结果:

A的类变量
多继承

查找顺序: C3算法

(3). __dict__

使用__dict__类变量查看对象的实例变量

class Student:
    name = '默认名字'
    age = 0

    def __init__(this, name, age):
        pass



student = Student('a', 10)
print(student.__dict__)

这种情况下, student对象无实例变量, 下面这种情况是有实例变量的

class Student:
    name = '默认名字'
    age = 0

    def __init__(this, name, age):
        this.name = name
        this.age = age


student = Student('a', 10)
print(student.__dict__)

‘name’: ‘a’, ‘age’: 10

使用__dict__类变量查看类变量

print(Student.__dict__)

__module__: '__main__', 'name': '默认名字', 'age': 0, '__init__': <function Student.__init__ at 0x000002A23B62E950>, '__dict__': <attribute '__dict__' of 'Student' objects>, '__weakref__': <attribute '__weakref__' of 'Student' objects>, '__doc__': None

8. 类中函数的第一个参数

类中方法的一个参数代表类本身, 它用来访问类中变量, 必须要创建, 它代表当前这个对象

当调用类中的方法时, 不用传值

实例方法: 对象可以调用的方法, 第一个参数必须指定, 代表当前这个对象

9. 类变量

类变量定义方式: 在类中定义 或 类外使用类名.xxx = vvv

class Student:
    pass

Student.a = 5
print(Student.a)

结果:

5

直接在类外定义了一个类名变量, 这样的方式也可以定义实例变量

类变量使用方式: 类.变量名self.__class__.变量名

class Student:
    name = '默认名字'
    age = 0

    def __init__(this, name, age):
        this.name = name
        this.age = age
        print(Student.name)
        Student.name = 5
        print('类中访问', this.__class__.name)
student = Student('a', 10)
print('类外访问', Student.name)

结果:

默认名字
类中访问 5
类外访问 5

9. 类函数(类方法)

(1). 定义

定义类方法, 使用@classmethod装饰器

类方法的第一个参数代表类本身

class Student:
    studentCount = 0

    @classmethod
    def fun(cls):
        cls.studentCount += 1
        print(cls.studentCount)

(2). 使用

Student.fun()

类方法关联类变量

实例方法关联实例变量

使用场景

class Date:
    def __init__(self, year, month, day):
        self.year = year
        self.month = month
        self.day = day

    @classmethod
    def fromString(cls, string):
        year, month, day = string.split('-')
        return cls(year, month, day)

    def __str__(self):
        return f"self.year/self.month/self.day"

date = Date.fromString('2022-6-5')
print(date)

结果:

2022/6/5

10. 静态方法

(1). 定义

定义类方法, 使用@staticmethod装饰器

class Student:
    studentCount = 0

    @staticmethod
    def fun(x):
        print(x)

静态方法第一个参数不代表类本身, 而是一个参数

(2). 使用

类和对象都可以调用静态方法

class Student:
    studentCount = 0

    @staticmethod
    def fun(x):
        print(x)

student = Student()
student.fun(2)
Student.fun(3)

静态方法可以使用类变量

使用场景

class Date:
    def __init__(self, year, month, day):
        self.year = year
        self.month = month
        self.day = day

    @classmethod
    def fromString(cls, string):
        year, month, day = string.split('-')
        return cls(year, month, day)
    
    @staticmethod
    def validateDateString(string):
        year, month, day = string.split('-')
        if (month < 31) and (month > -1):
            return True
        else:
            return False

    def __str__(self):
        return f"self.year/self.month/self.day"

date = Date.fromString('2022-6-5')
print(date)

结果:

2022/6/5

11. 布尔类型与类的关系

如果不手动写出这两个函数, 默认类对象的布尔值是True

实际上, 对象的布尔值取决与__bool__()方法的返回值, 如果没有该方法, 取决于__len__()方法的返回值

在魔法函数中详细解释

12. 魔法函数

在类中, 以__双下划线开头和结尾的函数, 用于制定类的特性, 这些方法的调用是隐式的

使用hasattr(对象, 函数名) -> bool方法可以检查该类对象是否有指定的函数

(1). 分类

非数字类
类别函数
字符串表示__repr__, __str__
集合序列相关__len__, __getitem__, __setitem__, __delitem__, __contains__
迭代__iter__, __next__
可调用__call__
with上下文管理器__enter__, __exit__
数值转换__abs__, __bool__, __int__, __float__, __hash__, __index__
元类相关__new__, __init__
属性相关__get__, __set__, __delete__
协程__await__, __aiter, __aenter__, __aexit__
数字类
类别函数
一元运算符__neg__取反, __pos__取正, __abs__绝对值
二元运算符__lt__小于, __le__小于等于, __eq__等于, __ne__不等于, __gt__大于, __ge__大于等于
算术运算符__add__加法, __sub__减法, __mul__乘法, __truediv__除法, __floordiv__地板除, __mod__取余
算术运算符__divmod__divmod()函数, __pow__乘方, __round__四舍五入取整
反向算术运算符__radd__, __rsub__, __rmul__, __rtruediv__, __rfloordiv__, __rmod__, __rdivmod__, __rpow__
增量赋值算数运算符__iadd__加法, __isub__减法, __imul__乘法, _i_truediv__除法, __ifloordiv__地板除, __imod__取余, __ipow__乘方
位运算__invert__取反, __lshift__左移, __rshift__右移, __and__且, __or__或, __xor__异或
反向位运算__rinvert__, __rlshift__, __rrshift__, __rand__, __ror__, __rxor__
增量赋值位运算符__ilshift__左移, __irshift__右移, __iand__且, __ior__或, __ixor__异或

(2). str(self)

函数作用
str(self) -> 字符串对对象调用str()函数或者对象字符化时, 会隐式调用该方法

实际上, 调用print函数时, 首先会调用一次str函数

class Company:
    def __init__(self, employeeList):
        self.employee = employeeList
    def __str__(self):
        return "123"

company = Company(['tom', 'cat', 'bill'])
# 调用print会隐含调用str(company)
print(company)

结果:

123

(3). repr(self)

函数作用
repr(self) -> 字符串打印时会隐式调用该方法

__str__()方法没有定义时, 对对象使用str()函数会调用该方法

在idle中直接输入变量, 会打印出结果, 这时调用的是该方法

class Company:
    def __init__(self, employeeList):
        self.employee = employeeList
    def __repr__(self):
        return '__repr__方法'

    # def __str__(self):
    #     return '__str__方法'

company = Company(['tom', 'cat', 'bill'])
print(company)
print(str(company))

结果:

__repr__方法
__repr__方法

(4). getitem(self, item)

函数作用参数解释
getitem(self, item) -> any可以允许该类对象使用[]访问类中数据, 访问的方式是自己实现, 实际类中有该方法, 该类对象是一个可迭代对象item: 索引值, 即a[0]中的0

类中有该方法, 该类对象是一个可迭代对象

使用索引方式访问对象,调用了__getitem__函数

实际上, 实现了__iter__函数才表示该类是一个迭代器, 可以用for-in遍历

但是python做了优化, 如果没有实现这个方法, 就会调用__getitem__方法

class Company:
    def __init__(self, employeeList):
        self.employee = employeeList

    def __getitem__(self, item):
        return self.employee[item]

company = Company(['tom', 'cat', 'bill'])
for em in company:
    print(em)

结果:

tom
cat
bill

当然, 你可以随便定义函数体

class Company:
    def __init__(self, employeeList):
        self.employee = employeeList

    def __getitem__(self, item):
        print('item的值', item)
        return '调用了__getitem__函数'

company = Company(['tom', 'cat', 'bill'])
print(company[2.5])

结果:

item的值 2.5
调用了__getitem__函数

(5). len(self)

函数作用参数解释
len(self) -> int或bool当对对象调用len()函数时隐式调用该函数, 如果没有__bool__函数时, 对对象调用bool()函数时也会隐式调用该函数

该方法的返回值是: int 和 bool 类型, 它与对象的布尔值相关

class A:
    def __len__(self):
        return 1
print(bool(A()))


class B:
    def __len__(self):
        return 0
print(bool(B()))

class C:
    def __len__(self):
        return True
print(bool(C()))

class D:
    def __len__(self):
        return False
print(bool(D()))

结果:

True
False
True
False

对对象使用bool()len()函数时, 实际上调用的是__len__()方法

class A:
    def __len__(self):
        return 1
print(bool(A()))
print(bool(len(A())))

结果:

True
True

但是, 如果没有手动定义__len__()方法, 不能够对对象调用len()函数

(6). bool(self) 方法

函数作用参数解释
bool(self) -> bool当对对象调用bool()函数时会隐式调用该函数

该函数的返回值直接决定该类对象的布尔值

该函数的返回值必须是bool类型

class A:
    def __len__(self):
        return True
    def __bool__(self):
        return False
a: A = A()
print(bool(a))

结果:

False

13. 访问修饰符

(1). 私有变量

默认情况下, 定义的变量和函数可以在类外直接访问

使用__开头的且结尾不是以__结尾的变量或函数, 系统会认为是私有的, 不可以在类外直接访问

class Student:
    __studentCount = 2

    def __fun(self):
        print(self.__studentCount)

student = Student()
print(student.__studentCount)
student.__fun()

这样访问是错误的, 会报错

注意下面这种情况

class Student:
    __studentCount_ = 2

    def __fun(self):
        print(self.__studentCount_)

student = Student()
student.__studentCount_ = 5
print(student.__studentCount_)

结果

5

为什么这种情况可以直接访问呢?

因为student.__studentCount_ = 5语句动态为student对象添加了一个__studentCount_变量, 所以可以访问

查看一下, 改一下代码

class Student:
    __studentCount_ = 2

    def __fun(self):
        print(self.__studentCount_)

student = Student()
student.__studentCount = 5
print(student.__studentCount)
print(student.__dict__)

5

‘__studentCount’: 5

(2). 类外访问私有变量

最好不要这么做, 不然定义私有变量就无意义了

访问方法_类名 + 私用变量名

class Student:
    __v = '私有变量'

    def fun(self):
        print(self.__v)

student = Student()
print(student._Student__v)

结果

私有变量

14. 继承

python支持多继承, 即一个子类可以继承多个父类

语法:

class 类名(继承的类名):
    pass
class People:
    pass

class Student(People):
    pass

object类是所有类都要继承的一个基础类

使用__bases__内置变量查看父类

class People:
    pass

class Student(People):
    pass

print(People.__bases__)
print(Student.__bases__)以上是关于学习 Python 之 面向对象的主要内容,如果未能解决你的问题,请参考以下文章

Python进阶(十六)----面向对象之~封装,多态,鸭子模型,super原理(单继承原理,多继承原理)

面向对象之鸭子类型

面向对象之多态以及魔法函数

Python面向对象编程——多态多态性鸭子类型

面向对象-鸭子类型

面向对象之组合封装多态性鸭子类型