学习 Python 之 面向对象

书读的越多而不加思考,你就会觉得你知道得很多;而当你读书而思考得越多的时候,你就会越清楚地看到,你知道得很少。

导读:本篇文章讲解 学习 Python 之 面向对象,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com,来源:原文

学习 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__)

结果:

(<class 'object'>,)
(<class '__main__.People'>,)

(1). 变量和函数继承性

子类继承父类的全部变量和函数

class People:

    __name = None

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

    def getName(self):
        return self.__name

class Student(People):
    pass

student = Student('张三')
print(student.getName())

(2). 构造函数

子类调用父类构造函数, 使用super()

class People:

    __name = None

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

    def getName(self):
        return self.__name

class Student(People):
    def __init__(self, school, name):
        self.__school = school
        super(Student, self).__init__(name)
        super().__init__(name)

    def getSchool(self):
        return self.__school

student = Student('张三', '光明小学')
print(student.getName(), student.getSchool())

结果

光明小学 张三

子类调用父类构造函数, 使用父类名.__init__(self, 参数)

class People:

    __name = None

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

    def getName(self):
        return self.__name

class Student(People):
    def __init__(self, school, name):
        self.__school = school
        People.__init__(self, name)

    def getSchool(self):
        return self.__school

(3). super()

super()表示父类引用

当父类和子类有同名函数时, 默认情况调用的是子类函数, 如果要调用父类的函数, 可以使用super()

枚举

枚举是一个类, 我们可以继承它, 实现枚举类

from enum import Enum

class Vip(Enum):
    Yellow = 1
    Green = 2
    Black = 3
    Red = 4

1. 继承枚举类与普通类的区别

(1). 枚举类中的变量打印出来的不是对应的值, 而是变量名字

from enum import Enum

class Vip(Enum):
    Yellow = 1
    Green = 2
    Black = 3
    Red = 4

# print(Vip['Yellow'])
print(Vip.Yellow)

Vip.Yellow

(2). 枚举类中的变量值不可改变

from enum import Enum

class Vip(Enum):
    Yellow = 1
    Green = 2
    Black = 3
    Red = 4

Vip.Yellow = 5

会报错:

AttributeError: Cannot reassign members.

(3). 枚举类可以防止相同变量

from enum import Enum

class Vip(Enum):
    Yellow = 1
    Yellow = 1
    Black = 3
    Red = 4

print(Vip.Yellow)

会报错:

TypeError: Attempted to reuse key: ‘Yellow’

(4). 枚举类变量的值相同, 后定义的为别名

from enum import Enum

class Vip(Enum):
    Yellow = 1
    Green = 1
    Black = 3
    Red = 4

print(Vip.Green)

结果

Vip.Yellow

可以看出 Green 输出的值和 Yellow 的一样

可以认为 Green 是 Yellow 的别名

2. 枚举类访问与遍历

(1). 获取枚举类中变量的值

from enum import Enum

class Vip(Enum):
    Yellow = 1
    Black = 3
    Red = 4

print(Vip.Yellow.value)

1

(2). 获取枚举类中变量的名字

from enum import Enum

class Vip(Enum):
    Yellow = 1
    Black = 3
    Red = 4

print(Vip.Yellow.name)

Yellow

(3). Vip.Yellow.name 与 Vip.Yellow 的区别

类型不一样

from enum import Enum

class Vip(Enum):
    Yellow = 1
    Black = 3
    Red = 4

print('Vip.Yellow.name 的类型', type(Vip.Yellow.name))
print('Vip.Yellow 的类型', type(Vip.Yellow))
print("Vip['Yellow'] 的类型", type(Vip['Yellow']))

Vip.Yellow.name 的类型 <class ‘str’>

Vip.Yellow 的类型 <enum ‘Vip’>

Vip[‘Yellow’] 的类型 <enum ‘Vip’>

(4). 枚举类可以遍历

from enum import Enum

class Vip(Enum):
    Yellow = 1
    Green = 2
    Black = 3
    Red = 4
    Blue = 3

for e in Vip:
    print(e)

Vip.Yellow

Vip.Green

Vip.Black

Vip.Red

可以看出Blue没有被打印出来

3. 枚举之间的比较

(1). 等值比较

枚举之间支持 ==等号比较

from enum import Enum

class Vip(Enum):
    Yellow = 1
    Green = 2
    Black = 3
    Red = 4

print(Vip.Green == Vip.Yellow)

(2). 不支持大小比较

不能使用 <, > 等符号比较

4. 枚举类型内置变量 members

获取枚举类下所以变量, 别名也获取

from enum import Enum

class Vip(Enum):
    Yellow = 1
    Green = 1
    Black = 3
    Red = 4

for e in Vip.__members__:
    print(e)
    
for e in Vip.__members__.items():
    print(e)

结果:

Yellow

Green

Black

Red

(‘Yellow’, <Vip.Yellow: 1>)

(‘Green’, <Vip.Yellow: 1>)

(‘Black’, <Vip.Black: 3>)

(‘Red’, <Vip.Red: 4>)

看一下对应的类型

from enum import Enum

class Vip(Enum):
    Yellow = 1
    Green = 1
    Black = 3
    Red = 4

print('Vip.__members__ 的类型', type(Vip.__members__))
print('Vip.__members__.items() 的类型', type(Vip.__members__.items()))

Vip.members 的类型 <class ‘mappingproxy’>

Vip.members.items() 的类型 <class ‘dict_items’>

4. 枚举转换

本质: 使用值创建枚举对象

from enum import Enum

class Vip(Enum):
    Yellow = 1
    Green = 1
    Black = 3
    Red = 4

a = 1
ea = Vip(a)
print(ea)

Vip.Yellow

6. 枚举类扩展

(1). IntEnum枚举类

继承后, 枚举类中的值只能是int类型

from enum import IntEnum

(2). 不运行枚举类中有别名

使用unique装饰器


from enum import Enum, unique

@unique
class Vip(Enum):
    Yellow = 1
    Green = 1
    Black = 3
    Red = 4

此时会报错:

ValueError: duplicate values found in <enum ‘Vip’>: Green -> Yellow

(3). 枚举类型是单例设计模式实现

只能有一个对象

from enum import Enum, unique

@unique
class Vip(Enum):
    Yellow = 1
    Green = 2
    Black = 3
    Red = 4

a = 1

e = Vip(a)

print(id(e.Yellow))

b = 2

e2 = Vip(b)

print(id(e2.Yellow))

2070070533984

2070070533984

发现Yellow的地址是一样的

python多态

1. 鸭子类型

鸭子类型就是该类的类型只跟魔法函数实现相关, 即魔法函数决定了这个类的类型

class Dog:
    def say(self):
        print('狗')

class Cat:
    def say(self):
        print('猫')

class Duck:
    def say(self):
        print('鸭子')


animalList = [Dog(), Cat(), Duck()]
for animal in animalList:
    animal.say()

结果:

狗
猫
鸭子

2. 抽象基类

抽象基类是 python中的abc模块, 即 abstract base class

抽象基类是不能实例化, 继承抽象基类的类必须实现里面的方法

这类似java的抽象类

应用场景:

  1. 检查类是否有某种方法
  2. 强制子类实现某些方法

(1). 不使用抽象基类强制子类实现某个方法

class AbstractClass:
    def get(self):
        raise NotImplementedError
    def set(self):
        raise NotImplementedError

class A(AbstractClass):
    pass

a = A()
a.set()

这种情况在子类对象调用这些方法时才会抛出异常, 不调用依然可以允许, 不算强制

(2). 使用抽象基类强制子类实现某个方法

class AbstractClass(metaclass = abc.ABCMeta):
    @abc.abstractmethod
    def get(self):
        pass
    @abc.abstractmethod
    def set(self):
        pass

class A(AbstractClass):
    pass

a = A()

实例在初始化的时候就会出现异常

结果:

TypeError: Can't instantiate abstract class A with abstract methods get, set

(3). Python定义好的抽象基类

在 collections.abc模块下面

下面是 abc 模块中定义的抽象基类

__all__ = [
    "Awaitable",
    "Coroutine",
    "AsyncIterable",
    "AsyncIterator",
    "AsyncGenerator",
    "Hashable",
    "Iterable",
    "Iterator",
    "Generator",
    "Reversible",
    "Sized",
    "Container",
    "Callable",
    "Collection",
    "Set",
    "MutableSet",
    "Mapping",
    "MutableMapping",
    "MappingView",
    "KeysView",
    "ItemsView",
    "ValuesView",
    "Sequence",
    "MutableSequence",
    "ByteString",
]

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/122788.html

(0)
飞熊的头像飞熊bm

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!