学习 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
注意:
- 构造函数只能返回None
- 构造函数可以显式调用, 但是返回值是None
- 定义了构造方法后, 如果构造方法需要传入参数, 创建对象时必须传入参数
7. 类变量与实例变量
(1). 理解类变量与实例变量
类变量: 与类相关的变量
实例变量: 与对象相关的变量, 即对象可以访问的变量
简单理解: 类中直接定义的变量是类变量, 使用self.xxx定义的变量是实例变量
学生类中, x
和y
是类变量
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). 不使用抽象基类强制子类实现某个方法
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