Python面向对象编程 (上)
写在前面
参考视频:https://www.bilibili.com/video/BV1A4411v7b2?p=31&vd_source=1a36db16e3fec3ccbe040303ff015aab 参考博客:https://blog.csdn.net/qq_41872653/article/details/109256914
Chapter1、面向对象基本概念
1、什么是面向对象编程?
面向对象编程(Object-Oriented Programming,OOP)是一种常用的编程思想,它强调万物皆对象,因此在编程时我们可以将现实世界中的事物抽象成程序中的对象,从而更好实现软件的设计与开发。与传统的基于函数的编程不同,面向对象编程注重于将数据与行为封装在一起,即对象既包含数据状态,还包含可调用的行为方法。
面向对象编程的特点在于,它具有封装、继承和多态三大特性。封装意味着将对象的状态和行为进行封装,使其对外只暴露必要的接口,从而提高了安全性和可维护性;继承指的是某个对象可以继承另一个对象的特性,从而快速构建具有相似属性的对象;多态是指同一种行为在不同的对象上具有不同的表现形式,即在不同的情境下,同一个方法可以被不同的对象进行调用。
总之,面向对象编程是一种强大的编程方式,它具有高度封装性、灵活的继承性和强大的多态性,通过使用对象作为程序的基本处理单元,实现了数据和行为的有机结合,可以使程序更加高效、结构清晰,并方便管理和扩展。
2、对象和类的关系
对象可以抽象出某个具体的类,类可以实例化成某个具体的对象。
Chapter2、类的基本语法
1 | class Dog: |
1、类的命名要求首字母大写。
1 | class Money: |
属性相关
2、属性相关概念
变量是可以改变的量值;属性是“属于某个对象的特性”。变量根据访问位置的不同,分为全局变量和局部变量;属性只能通过对象来访问,所以必须先找到对象————而对象又通过变量名来访问,故也遵循访问权限。
3、属性添加
1 | class Person: |
4、属性修改
1 | class Person: |
当我们修改没有创建过的属性时: 1
2print(p.sex)
报错AttributeError:1
2
3
4
5
6
7class Person:
pass
p = Person()
p.age = 18
del p.age
print(p.age)#AttributeError:
先删除属性,再删除引用的变量。
P14、注意事项
不同对象的属性不能互相访问。
P15、类属性-增加属性
万物皆对象,类也可以是一个对象。
注意,当我们创建一个类的时候,我们实际上也创建了一些类的内置属性(name,__module__等)
1
2
3
4class Money:
pass
Money.count = 1 #Money看做对象,Money增加count属性
print(Money.count)
P17、类属性查询
1 | class Money: |
注意:一个对象,如果要去访问某个属性,它会优先查找自己有没有这个属性,有就返回;若没有,则在类中找。都没有就报错AttributeError:
P18、类属性修改
1 | class Money: |
注意:不可通过对象修改! one是一个对象,one.age=99是给one对象增加一个age指向99的属性,而不是修改类的属性。
P19、类属性删除
1 | del Money.age |
注意:不能通过对象来删除。
总结类属性操作
增、删、改类属性都只能通过类。查询可通过对象和类操作。
P20、类属性的内存存储
1)one访问某个属性,实际上访问的是生成的对象里面__dict__字典的值。
1
2
3
4
5
6class Money:
pass
one=Money()
one.__dict__={'age':99}
print(one.age)
>>>991
2
3
4
5class Money:
age=20
Money.__dict__={'sex':'男'}
>>>报错
P21、类属性会被各个对象所共享
1 | class Money: |
P23、限制对象属性的添加__slots__
1 | class Person: |
方法相关
P25、概念和作用
1.描述一个目标的行为动作
2.和函数非常类似,都封装了一系列动作,都可以被调用之后执行一系列行为动作,最主要的时调用方式
1
2
3
4
5
6
7class Person:
def eat2(self):
print(1)
print(2)
print(3)
p = Person()
p.eat2()
P26、类、对象、类对象、实例对象、实例的叫法规范
P27、方法的划分
· 实例方法:第一个参数接受实例 · 类方法:第一个参数接受类 ·
静态方法:non 划分的依据:方法的第一个参数必须接受的数据类型
1
2
3
4
5
6
7
8
9
10class Person:
def eat2(self):
print('这是一个实例方法',self)
def leifangfa(cls):
print('这是一个类方法',cls)
def jiangtaifanfa():
print('这是一个静态方法')1
2Person.eat2()
>>>TypeError #确少self
P28、方法的储存
(1)函数也是对象 (2)方法也保存在类的__dict__中,没有在实例当中的
P29、要求
使用注意: 1.语法 2.不同类型的方法的规则 3.不同方法的调用
P30、实例方法
1 | #标准调用 |
P31、类方法
1 | class Person: |
P32、静态方法
1 | class Person: |
P33、不同类型的方法中访问不同类型的属性的权限问题
1 | class Person: |
类相关补充
P34、补充-元类
类也是由类创建出来的,这个类就是元类。 1
2
3
4
5
6
7
8
9
10
11
12
13
14num = 1
print(num.__class__) #输出int
%------------------------------------------
s = "abc"
print(s.__class__) #输出str
%------------------------------------------
class Person:
pass
p = Person()
print(p.__class__) #输出Person
%------------------------------------------
print(int.__class__) #输出type
#其实也是num.__class__.__class___
print(type.__class__)#依然输出type
P35、补充-类对象的创建方式
可以class直接创建 也可以调用type函数创建 1
2
3
4
5
6
7
8
9def run(self):
print(self)
xxx = type("Dog",(),{"count":0,"run":run})
print(xxx)
print(xxx.__dict__)
d.xxx() #不是d.Dog
print(d)
d.run()
P36、类对象创建时,元类的查找机制
1.检测类对象是否有明确的__mateclass__属性。
2.检测父类中是否存在__mateclass__属性。
3.检测模块中是否存在__mateclass__属性。
4.通过内置的type这个元类来创建这个类对象 1
2
3
4
5
6
7
8
9
10
11
12
13
14查找一般是现在自己里面找,没有就去父类,父类没有就往上找模块,模块没有就是type
#1模块级别的指定
__metaclass__ = xxx
#2
class Animal:
pass
#3
class Dog(Animal):
pass
#4
class Dog:
__metaclass__ = xxx
pass
P37、补充-类的描述
1 | class Person: |
P38、生成注视文档
属性相关补充
P39、补充-私有化属性的概念和意义
限定属性的访问的方法
P40、补充-访问权限测试区域划分
P41、补充-访问权限测试区域划分
注意:python并没有真正的私有化支持,但是,可以使用下划线完成伪私有的效果。如_y、__z。 一个_为保护,两个_为私有化
P41、私有化属性-共有属性
1.类的内部访问,也就是类的实例方法中 2.子类(延伸类)内部访问 3.模块其他位置访问 * 1.类访问(父类,派生类) * 2.实例访问(父类实例,派生类实例 4.跨模块访问 import/from xxx import *
1 | class Animal: |
P42、私有化属性-受保护的属性
_y:一个下划线 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29class Animal:
_x = 10
def test(self):
print(Animal._x)
print(self._x)
pass
#--------------------
class Dog(Animal):
def test2(self):
print(Dog._x)
print(self._x)
pass
a = Animal()
a.test() #类的内部访问
d = Dog()
d.test2() #延伸类的访问
print(Animal._x) #模块的其他部分,可以强行访问,有警告
print(Dog._x)
print(a._x)
print(b._x)
import xxx #模块的其他部分访问,不可访问
print(xxx.a)
from xxx import *
print(a)
43、私有化保护-私有属性
1 | class Animal: |
45、补充-私有属性的应用场景
1 | class Person: |
P48、只读属性-方案1
1 | class Person: |
P49、只读-方案一优化
这节讲得真好,承上启下。 1
2
3
4
5
6
7
8
9
10
11## 49-python-面向对象-只读属性-方案一的优化
1. ```python
class Person(object):
def __init__(self):
self.__age = 18
# 主要作用:可以以使用属性的方法来使用这个方法
def age(self): #部分公开,通过方法实现
return self.__age
p1 = person()
print(p1.age())
P50、property的作用
property的作用:将“一些属性的操作方法”关联到另一个属性,间接地管理私有属性。
1
2
3
4
5
6class C(object):
def getx(self): return self._x
def setx(self,value): self._x = value
def delx(self): del self._x
x = property(getx, setx, delx)
#此时,x就是一个属性,可以直接调用x的属性来调用方法
P51、经典类和新式类
- 经典:没有继承object
- 新式:继承object
Py3中,定义一个类,已经隐式地继承object,默认下已经是一个新式类。
1
2
3
4class Person:
pass
print(Person.__bases__) #查看父类(基类)
P52、property在新式类中的使用
- property函数方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15class Person(object):
def __init__(self):
self.__age = 18
def get_age(self):
ruturn self.__age
def set_age(self, value):
self.__age = value
age = property(get_age, set_age)
p = Person()
print(p.age)
p.age = 30
print(p.age)
print(p.__dict__) #输出只有_person__age: - 装饰器方法
1
2
3
4
5
6
7
8
9
10
11
12
13class Person(object):
def __init__(self):
self.__age = 18
def age(self):
return self.__age
def age(self, value):
delf.__age = value
p = Person()
p.age = 20 #既可以读取也可以设置
P53、property在经典类中的使用
放弃吧,都用新式类叭!
P54、只读属性-方案二
总之就是,__ age只不过是把他改了个名字,改成_ 类名 __
age,你知道他的新名字的话你当然可以改。 1
2
3
4
5
6
7
8
9
10
11
12
13class Person:
#当我们通过实例.属性 = 值,给一个实力增加一个属性,或者修改属性的时候,就会自动调用这个方法,
#在这个方法内部才会正真地把这个属性,以及对应的数据存储到__dict__字典里面
def __settter__(self, key, value):
print(key, value)
#1.判定,key,是否是我们要设置的只读属性
if key = "age" and key in self.__dict__.keys():#只能新增属性,不能修改属性
print("这个属性是只读属性,不能设置数据")
#2.如果不是只读属性,就给他添加到实例里面去
else:
#self.key = value #这样会陷入死循环
self.__dict__[key] = value
P55、常用内置属性
- 类属性:
- dict:类属性
- bases:类的所有父类构成元组
- __doc__类的文档字符
- __name__类名
- __module__类定义所在模块
- 实例属性
- dict:类的实例
- class:实例对应的类
1 |
内置方法
内置方法的作用;完成一些特定的功能
P57、信息格式化操作-* __ str __ *
1 | class Person: |
P58、信息格式化操作-* __ repr __ *
主要面向开发者 1
2
3
4def Person:
def __init__(self):
return"repr"
P60、* __ call __ *
__call__的作用:使得对象具有当作函数来调用的能力(将对象变为可调用对象)。当你把对象写出调用函数的形式,就会自动去找call
1
2
3
4
5
6
7class Person:
def __call__(self):
print("xxx")
pass
p = Person()
p() #调用__call__函数
P61、call的使用例子
1 | class PenFactory: |
P62、索引操作
需要一个字典属性,才可以对对象进行索引操作 1
2
3
4
5
6
7
8
9
10
11
12class Person:
def __init__(self):
self.cache = {} #定义cache属性,为一个字典
def __setitem__(self, key, value):
self.cache[key] = value
def __getitem__(self, key):
return self.cache[key]
def __delitem__(self, key):
del self.cache[key]
p = Person()
p['name'] = 'sz' #执行这行代码的时候,会调用第一个方法
print(p['name']) #调用第二个方法
P63、切片操作
没听懂orz 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15class Person:
def __init__(self):
self.items = [1, 2, 3, 4, 5, 6, 7, 8] #定义了一个items属性
def __setitem__(self, key, value):
if isinstance(key,silce):
self.items[key] = value #注意切片操作只能修改,不能新增
#self.items.[key.start: key.stop: key.step] = value #这样赋值也可以
def __getitem__(self, key):
return(key.items[key]) #这里可能有错
def __delitem__(self, key):
del self[key]
p = Person()
p[0: 4: 2] = ["a","b"]
print(p.items[0: 4: 2])
P64、比较操作
映射的内置方法 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17class Person:
def __init__(self, age, height):
self.age = age
self.height = height
def __eq__(self, other):
return self.age == other.age #指定相等的比较通过哪个属性比较,
def __ne__(self, other):
return self.age != other.age #指定相等的比较通过哪个属性比较,
p1 = Person(18, 180)
p2 = Person(19, 183)
print(p1 == p2)
#类似的还有:
#gt >
#ge >=
#lt <
#le <=
#可以通过调换参数的方式定义比较方法,进而简化代码
P65、比较操作
注意到p1 < p2,等价于p2 > p1。 如果对于反向操作的比较符,只定义了其中一个方法,但使用的是另外一种比较运算,那么,解释器会采用调换参数的方式进行调用该方法。
P66、比较操作-方案2(通过装饰器自动补全)
解决小于等于,大于等于的反向操作。 1
2
3
4
5
6
7
8import functools
#此装饰器会自动补全所需要的方法
class Person
def __lt__(self, other):
pass
def __eq__(self, other):
pass
P67、上下文布尔值
此方法使对象可以被作为一个布尔值使用。 1
2
3
4
5
6
7
8
9class Person():
def __bool__(self): #通过bool值判定实例是True或False
return False #这里也可以是一个返回布尔值的语句
pass
p = Person()
if p:
print("xx")
>>>不打印1
2
3
4
5
6
7
8
9
10class Person:
def __init__(self):
self.age=20
def __bool__(self):
if self.age >= 18:
return True
p = Person()
if p:
print("已成年")
P68、遍历操作-getitem
1 | class Person: |
P69、遍历操作-iter
iter优先级高于getitem 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15class Person:
def __init__(self):
self.result = 1
def __getitem__(self, item):
self.result += 1
if self.result >=6: #不满足时,跳出循环
raise StopIteration("停止遍历")
return relf.result
def __iter__(self):
print("hfkjasdf")
p = person()
for i in p: #当执行for循环时,自动进入__geritem__方法,使用这个方法的返回值作为数据
print(i)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20class Person:
def __init__(self):
self.result = 1
def __getitem__(self, item):
self.result += 1
if self.result >=6: #不满足时,跳出循环
raise StopIteration("停止遍历")
#return iter([1,2,3,4]) 如果返回是迭代器,则自动进入迭代器的__next__方法
return relf.result
def __iter__(self):
return self
def __next__(self):
self.result += 1
if self.result >=6: #不满足时,跳出循环
raise StopIteration("停止遍历")
return relf.result
p = person()
for i in p:
print(i)
P70、便利操作-直接next遍历
1 | class Person: |
P71、便利操作-迭代器的复用
1 | class Person: |
通过item里面的age归位,调为初始值。否则第一次迭代后age就变为终止值了,不满足迭代的条件。
P72、遍历操作-迭代器-可迭代的判定依据
1 | import collection |
一个可迭代对象一定可以通过for in访问,可以通过for in访问的不一定是可迭代对象。
P73、遍历操作-iter()的使用
我超这一点都不便利www
P74、描述器
概念和作用:描述器是一个对象,可以描述一个属性的操作;其作用是对属性的操作做验证和过滤,如对一个人的年龄赋值时,不能赋值为负数,这是需要验证和过滤,但由于属性数量多,不能在赋值前进行验证,所以用到描述器,每次验证时就会进入描述器中进行相关操作
P75、描述器-定义方式1-property
1 | class Person: |
P76、描述器-定义方式2-封装
1 | class Age: #描述器类 |
P77-P79、调节细节
P80、调节优先级
- 资料描述器 get set
- 非资料描述器 仅仅实现了get
- 资料>实例>非资料
P81、数据存储问题
弹幕:这么理解吧,age=Age(),本身这个就是在建立相同的对象,就是self打印的地址一样,instance就是self下的小单元,操作这个每个对象才有不同值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28class Age:
def __get__(self, instance, owner):
print("get")
return self.v
def __set__(self, instance, value):
print("set")
self.v = value
def __delete__(self, instance):
print("delete")
class Person:
age = Age()
p = Person()
p.age = 10
print(p.age)
>>>set
get
10
--------------------------------------------------
p2 = Person()
p2.age = 11
print(p2.age)
>>>11
这时候如果再打印p的话
>>>11
因为都是储存在age里,也就是Age只有一个对象self,即age1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16如果绑定在instance上的话:
class Age:
def __get__(self, instance, owner):
print("get")
return instance.v
def __set__(self, instance, value):
print("set")
instance.v = value
def __delete__(self, instance):
print("delete")
class Person:
age = Age()
就可以分别打印,分别存储
引入:在不改变某个def函数本身的情况下,增加这个函数的功能,如何?
注意层级,def
fashuoshuo()是函数并不是方法,fashuoshuo接收了check,所他自己就是check实例了
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15def check(func):
def inner():
print("登录成功")
func()
return inner
def fashuoshuo():
print("发说说")
fashuoshuo()
>>>
"登录成功"
"发说说"