Author:AXYZdong 硕士在读 工科男
有一点思考,有一点想法,有一点理性!
定个小小目标,努力成为习惯!在最美的年华遇见更好的自己!
CSDN@AXYZdong,CSDN首发,AXYZdong原创
唯一博客更新的地址为: 👉 AXYZdong的博客 👈
B站主页为:AXYZdong的个人主页
文章目录
- 前言
- 0. 什么是对象(object)?
- 1. 面向过程和面向对象
- 2. 类(class)
- 3. 属性(attribute)
- 3.1 属性和变量的区别
- 3.2 对象属性
- 3.3 类属性
- 3.4 对象属性和类属性对比
- 3.5 限制对象属性的添加
- 4. 方法
- 4.1 什么是方法?
- 4.2 实例方法
- 4.3 类方法
- 4.4 静态方法
- 4.5 不同类型的方法访问不同类型的属性
- 总结
- 参考文献
前言
本文为面向对象在Python中的实践,所使用的 环境为Python3.7。
参考视频:https://www.bilibili.com/video/BV1A4411v7b2
本文主要是对视频内容的整理与自我总结和体会。
0. 什么是对象(object)?
“把一组数据结构和处理它们的方法组成对象(object),把相同行为的对象归纳为类(class),通过类的封装(encapsulation)隐藏内部细节,通过继承(inheritance)实现类的特化(specialization)和泛化(generalization),通过多态(polymorphism)实现基于对象类型的动态分派。”
——比较正式的说法
更通俗易懂的说法,下面这段内容来自于知乎。
一句话说明什么是面向对象?你个土鳖,你们全家都是土鳖!
好像有人说过这样的话,当头棒喝的方式虽然情感上不易接受,但记忆效果十分显著。
好吧,如果你觉得“土鳖”实在难听也不能准确定位你的档次,你可以自行将其替换为“土豪”,whatever。
面向对象思想有三大要素:封装、继承和多态。
- 封装:不管你是土鳖还是土豪,不管你中午吃的是窝头还是鲍鱼,你的下水都在你肚皮里,别人看不到你中午吃了啥,除非你自己说给他们听(或者画给他们看,whatever);
- 继承:刚说了,你个土鳖/豪,你们全家都是土鳖/豪。冰冻三尺非一日之寒,你有今天,必定可以从你爸爸爷爷那里追根溯源。正所谓虎父无犬子,正恩同学那么狠,他爹正日就不是什么善茬,更甭说他爷爷日成,明白了吗?
- 多态:哲学家说过,世上不会有两个一模一样的双胞胎。即使你从你父亲那里继承来的土鳖/豪气质,也不可能完全是从一个模子里刻出来的,总会有些差别。比如你爸喜欢蹲在门前吃面,你喜欢骑在村口的歪脖子树上吃,或者反过来。当然,也可能令尊爱吃龙虾鲍鱼时旁边有几个艺校小女生喝酒唱歌助兴,你可能更喜欢弄个街舞乐队来吹拉弹唱。
作者:成心文
链接:https://www.zhihu.com/question/19854505/answer/21143952
来源:知乎
万物皆对象,对象是具体的事物。
- 具有属性
- 具有行为(方法)
- 把很多零散的东西,封装成一个整体
也就是说一个对象具有静态特征(属性)和动态特征(行为)。
举例:
比如有个叫王二小的人,他有属性和行为。
他的属性有
- 姓名
- 年龄
- 身高
- 体重
- …
他的行为有
- 走路
- 吃饭
- 放羊
- …
Python中的体现
Python 是一门彻底的面向对象编程的语言(OOP:Object Oriented Programming)
在其他的语言中一般都包括基本数据类型和对象类型,而在Python中将它们统称为对象类型,包括 int、float、bool、list、dictionary…
1. 面向过程和面向对象
总的来说都是一种解决问题的思路,面向对象本身则是对面向过程的封装。
- 面向过程:在解决问题的时候,关注的是解决问题的每一个的过程(步骤)。
- 面向对象:在解决问题的时候,关注的是解决问题所需要的对象。
面向过程编程关键在于按照步骤划分,把一个任务分解成具体的每一个步骤。而面向对象编程的关键是按照功能将对象进行划分,找到对象,确定对象属性和行为。
从面向过程过度到面向对象
- 列举出一个任务的具体实现步骤
- 试图分离这些实现步骤中的功能代码块
- 将这些功能代码块,划分到某一个对象中
- 根据这个对象以及对应的行为,抽象出对应的类
2. 类(class)
简单的说,类是对象的蓝图和模板,而对象是类的实例。
这个解释虽然有点像用概念在解释概念,但是从这句话我们至少可以看出,类是抽象的概念,而对象是具体的东西。在面向对象编程的世界中,一切皆为对象,对象都有属性和行为,每个对象都是独一无二的,而且对象一定属于某个类(型)。
当我们把一大堆拥有共同特征的对象的静态特征(属性)和动态特征(行为)都抽取出来后,就可以定义出一个叫做“类”的东西。也就是某个具体对象特征的抽象。
类由名称、属性和方法组成,属性和方法是抽象的概念,而根据抽象的类可以产生具体的对象。这些对象才拥有具体的属性和方法。
生活中的类:
- 类:汽车。对象:大众、奥迪、马自达…
- 类:手机。对象:华为手机、小米手机…
- 类:熊类。对象:熊大、熊二…
- …
类和对象的关系
- 对象的 抽象化 形成类
- 类的 实例化 形成对象
▲ 类和对象关系
类的定义
>>>class People:
pass
>>>print(People)
<class '__main__.Name'>
- 类名的首字母需要大写
根据类创建(实例化)对象
>>>class People:
pass
>>>print(People)
<class '__main__.Name'>
>>>one = People()
>>>print(one)
<__main__.Name object at 0x000001790BF5B8E0>
从打印出来的结果可以看到 Name
是一个类,而 one
是一个对象。
▲ 实例化过程的底层内存图
3. 属性(attribute)
3.1 属性和变量的区别
1、概念
- 变量是“可以改变的量”
- 属性是“属于某个对象的特性”
2、访问权限
- 变量,根据不同的位置,存在不同的访问权限。全局变量、局部变量…
- 属性,只能通过对象来访问,因此必须先找到对象。对象也是通过变量名来引用,而既然是对象也有对应的访问权限。
判定依据
变量是否有宿主
- 有,则就是属性。而这个宿主就是对象。
- 否,则就是变量。
可以根据属性宿主的不同,将属性分为对象属性和类属性。如果属性的宿主为对象,则为对象属性。否则,是类属性。
3.2 对象属性
- 增
>>> class Person:
pass
>>> p = Person()
>>> p.age = 18 #增加age属性
>>> print(p.age)
18
>>> print(p.__dict__)
{'age': 18}
>>> p.hight = 180 #增加hight属性
>>> print(p.hight)
180
>>> print(p.__dict__)
{'age': 18, 'hight': 180}
- 查
>>> class Person:
pass
>>> p = Person()
>>> p.age = 18
>>> print(p.age)
18
>>> p.age = 20
>>> print(p.age)
20
>>> print(p.sex) #对象里面没有定义相关属性在查询时会报错
Traceback (most recent call last):
File "<pyshell#13>", line 1, in <module>
print(p.sex)
AttributeError: 'Person' object has no attribute 'sex'
- 改
对象.属性
,和增加对象属性类似,如果当前属性存在就是修改对象属性,如果当前属性不存在就是增加对象属性。
>>> class Person:
pass
>>> p = Person()
>>> p.num = [1,2,3]
>>> print(p.num,id(p.num))
[1, 2, 3] 1959642842376
>>> p.num = ['a','b','c']
>>> print(p.num,id(p.num))
['a', 'b', 'c'] 1959642842888
>>> p.num.append('d') #利用append方法则不会重新开辟空间
>>> print(p.num,id(p.num))
['a', 'b', 'c', 'd'] 1959642842888
修改对象属性,重新开辟一个空间,相当于修改了对应标签的地址。
- 删
del 对象.属性
>>> class Person:
pass
>>> p = Person()
>>> p.age = 18
>>> print(p.age)
18
>>> del p.age
>>> print(p.age)
Traceback (most recent call last):
File "<pyshell#21>", line 1, in <module>
print(p.age)
AttributeError: 'Person' object has no attribute 'age'
- 注意事项
利用对象.__dict__
查看对象的所有属性。
不同的对象之前不能互相访问对方的属性,否则会报错。
>>> class Person:
pass
>>> p = Person()
>>> p1 = Person()
>>> p2 = Person()
>>> p1.age = 18
>>> p2.address = 'shanghai'
>>> print(p1.address)
Traceback (most recent call last):
File "<pyshell#27>", line 1, in <module>
print(p1.address)
AttributeError: 'Person' object has no attribute 'address'
>>> p1.hight = 180
>>> print(p1.__dict__)
{'age': 18, 'hight': 180}
3.3 类属性
万物皆对相关,类也是一个"对象"。
- 增
方式一:类名.类属性 = 值
>>> class Money:
pass
>>> Money.count = 0
>>> print(Money.count)
0
>>> print(Money.__dict__) #利用 `Money.__dict__` 查看类的所有属性。
{'__module__': '__main__', '__dict__': <attribute '__dict__' of 'Money' objects>, '__weakref__': <attribute '__weakref__' of 'Money' objects>, '__doc__': None, 'count': 0}
方式二:直接在类定义里面添加
>>> class Money:
count = 18
num = 100
age = 20
>>> print(Money.count,Money.num,Money.age)
18 100 20
>>> print(Money.__dict__) #利用 `Money.__dict__` 查看类的所有属性。
{'__module__': '__main__', 'count': 18, 'num': 100, 'age': 20, '__dict__': <attribute '__dict__' of 'Money' objects>, '__weakref__': <attribute '__weakref__' of 'Money' objects>, '__doc__': None}
- 查
通过类访问:类名.类属性
通过对象访问:对象.类属性
>>> class Money:
count = 18
num = 100
age = 20
>>> one = Money()
>>> print(one.age ,one.count ,one.num )
20 18 100
▲ 类属性创建内存图
可以通过对象访问到类属性,和Python对象的属性查找机制有关。优先到对象自身查找属性,找到则结束。否则,根据__class__
找到对象对应的类,再到这个类里面查找。
>>> class Money:
count = 18
num = 100
age = 20
>>> one = Money()
>>> print(one.age ,one.count ,one.num )
20 18 100
>>> print(one.__class__)
<class '__main__.Money'>
>>> class Test:
hight = 178
>>> one.__class__ = Test
>>> print(one.hight)
178
- 改
通过类名改,语法和给类名增加一个属性的方式相似。如果属性不存在,则新增;如果存在,则修改。
类名.属性 = 值
>>> class Money:
count = 18
num = 100
age = 20
>>> Money.age = 22
>>> print(Money.age)
22
不能通过对象修改类属性。
- 删
del 类名.属性
>>> class Money:
count = 18
num = 100
age = 20
>>> del Money.age
>>> print(Money.age)
Traceback (most recent call last):
File "<pyshell#32>", line 1, in <module>
print(Money.age)
AttributeError: type object 'Money' has no attribute 'age'
>>> one = Money()
>>> del one.age
Traceback (most recent call last):
File "<pyshell#34>", line 1, in <module>
del one.age
AttributeError: age
>>> Money.age = 20
>>> one.age = 18
>>> del one.age
>>> print(one.age) #通过对象对应的类访问属性
20
不能通过对象删除类属性。
类属性的内存存储
一般情况下,属性存储在__dict__
的字典当中,可以通过 对象.__dict__
来查看所有的属性。
>>> class Money:
pass
>>> one = Money()
>>> one.age = 22
>>> one.hight = 180
>>> print(one.__dict__)
{'age': 22, 'hight': 180}
可以通过增加/修改__dict__
的字典当中的内容,从而控制实例对象的属性。
>>> class Money:
pass
>>> one = Money()
>>> one.age = 22
>>> one.hight = 180
>>> print(one.__dict__)
{'age': 22, 'hight': 180}
>>> one.__dict__ = {'name': 'axyzdong', 'age': 22, 'hight': 180}
>>> print(one.__dict__)
{'name': 'axyzdong', 'age': 22, 'hight': 180}
>>> one.__dict__['hight'] = 190
>>> print(one.__dict__)
{'name': 'axyzdong', 'age': 22, 'hight': 190}
无法通过直接修改__dict__
中的内容来改变类的属性。
>>> class Money:
count = 18
num = 100
age = 20
>>> print(Money.__dict__)
{'__module__': '__main__', 'count': 18, 'num': 100, 'age': 20, '__dict__': <attribute '__dict__' of 'Money' objects>, '__weakref__': <attribute '__weakref__' of 'Money' objects>, '__doc__': None}
>>> Money.__dict__ = {'name': 'axyzdong', 'age': 22, 'hight': 180} #直接修改会报错
Traceback (most recent call last):
File "<pyshell#53>", line 1, in <module>
Money.__dict__ = {'name': 'axyzdong', 'age': 22, 'hight': 180}
AttributeError: attribute '__dict__' of 'type' objects is not writable
>>> Money.__dict__['age'] = 20
Traceback (most recent call last):
File "<pyshell#54>", line 1, in <module>
Money.__dict__['age'] = 20
TypeError: 'mappingproxy' object does not support item assignment
可以使用 setattr 方法进行修改。
类属性被各个对象所共享
类属性修改之后,所有的对象访问到的类属性都会跟着修改.
>>> class Money:
age = 18
>>> one = Money()
>>> two = Money()
>>> print(one.age,two.age)
18 18
>>> Money.age = 20
>>> print(one.age,two.age)
20 20
3.4 对象属性和类属性对比
存储不同、抽象层级不同、宿主不同。
增删改查中“增删改”的语法基本相同,在“查”中,实例对象中如果没有该属性,则会到实例对象所对应的类中寻找类属性。
>>> class Person:
age = 18
>>> p = Person()
>>> p.age += 5 #先访问类对象中的属性,然后再创建实例对象的属性
>>> print(Person.age)
18
>>> print(p.age)
23
3.5 限制对象属性的添加
>>> class Person:
__slots__ = ['age']
pass
>>> p1 = Person()
>>> p1.age = 18
>>> print(p1.age)
18
>>> p1.num = 100
Traceback (most recent call last):
File "<pyshell#78>", line 1, in <module>
p1.num = 100
AttributeError: 'Person' object has no attribute 'num'
>>> p2 = Person()
>>> p2.age = 28
>>> print(p2.age)
28
>>> p2.hight = 180
Traceback (most recent call last):
File "<pyshell#82>", line 1, in <module>
p2.hight = 180
AttributeError: 'Person' object has no attribute 'hight'
__slots__ = ['age']
限制对象的属性只能有 age
,如果添加其他属性则会报错。其中列表中的元素,即为通过这个类创建出的对象可以添加的对象属性,如果这个类实例出的对象,添加了非列表之内的属性,则会报错。
4. 方法
4.1 什么是方法?
描述一个目标行为动作,和函数非常类似
- 都封装了一系列动作
- 都可以被调用之后,执行一系列动作
- 主要区别是调用方式的不同
>>> class Person:
def eat(self):
print(1)
print(2)
print(3)
>>> p1 = Person()
>>> p1.eat()
1
2
3
关于self
self表示当前对象
- self并不是一个关键字,其实可以是任意的标识符,为了表达代表的是当前对象自己,习惯用self
- 调用实例函数的时候,self不需要手动传参,系统会自动传递当前的对象
- 哪个对象调用了方法,方法里的self指的就是谁。
通过 self.属性名 可以访问到这个对象的属性。
通过 self.方法名() 可以调用这个对象的方法。
方法的划分
- 实例方法:默认第一个参数需要接收到一个实例
- 类方法:默认第一个参数需要接收到一个类
- 静态方法:第一个参数不需要默认接收
- 划分依据:方法的第一个参数必须要接收的数据类型;
- 不管是哪一种类型的方法,都是存储在类当中,不会是在实例当中;
- 不同类型方法的调动方法不同,但是最终要保证不同类型的方法第一个参数接收到的数据是他们想要的类型。
>>> class Person:
def func(self):
print('实例方法',self)
@classmethod
def classfunc(cls):
print('类方法',cls)
@staticmethod
def staticfunc():
print('静态方法')
>>> p = Person()
>>> p.func()
实例方法 <__main__.Person object at 0x000002413B224548>
>>> Person.classfunc()
类方法 <class '__main__.Person'>
>>> Person.staticfunc()
静态方法
方法的存储
不论是哪一种类型的方法都存储在类当中。
>>> class Person:
def func(self):
print('实例方法',self)
@classmethod
def classfunc(cls):
print('类方法',cls)
@staticmethod
def staticfunc():
print('静态方法')
>>> p = Person()
>>> p.func()
实例方法 <__main__.Person object at 0x000002413B224548>
>>> Person.classfunc()
类方法 <class '__main__.Person'>
>>> Person.staticfunc()
静态方法
>>> print(p.__dict__) #并不会存储在实例对象中
{}
>>> print(Person.__dict__)
{'__module__': '__main__', 'func': <function Person.func at 0x000002413B27F0D8>, 'classfunc': <classmethod object at 0x000002413B25B808>, 'staticfunc': <staticmethod object at 0x000002413B119288>, '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__': None}
在 Person.__dict__
中就包含了前面定义的 func
、classfunc
、staticfunc
三种不同类型的方法。而这些方法是不会存储在实例对象中的。
4.2 实例方法
-
按照
实例化对象.方法
的模式调用,必须传递一个对象,在调用方法之前,定义一个实例化对象.对象本身参数不需传。 -
也可以使用
类.方法
调用,这种方式相当于函数,每个参数都要传进去。
>>> class Person:
def eat(self,food):
print('在吃',self,food)
>>> p = Person()
>>> p.eat('apple') # 实例化对象.方法,不需要传self参数
在吃 <__main__.Person object at 0x000002413B0855C8> apple
>>> print(Person.eat)
<function Person.eat at 0x000002413B27F5E8>
>>> Person.eat('123','apple') # 类.方法,每个参数都要传,相当于一个函数
在吃 123 apple
4.3 类方法
官方描述
"""
classmethod(function) -> method
Convert a function to be a class method.
A class method receives the class as implicit first argument,
just like an instance method receives the instance.
To declare a class method, use this idiom:
class C:
@classmethod
def f(cls, arg1, arg2, ...):
...
It can be called either on the class (e.g. C.f()) or on an instance
(e.g. C().f()). The instance is ignored except for its class.
If a class method is called for a derived class, the derived class
object is passed as the implied first argument.
Class methods are different than C++ or Java static methods.
If you want those, see the staticmethod builtin.
"""
两种调用方式
- 类调用:不用手动传递第一个参数,会自动的把调用的类本身给传递过去
- 对象调用:不用手动传递第一个参数,会自动的把调用的对象所对应的类给传递过去
>>> class Person:
@classmethod
def classfunc(cls,a):
print('类方法',cls,a)
>>> Person.classfunc(111)
类方法 <class '__main__.Person'> 111
>>> p = Person()
>>> p.classfunc(123)
类方法 <class '__main__.Person'> 123
4.4 静态方法
- 类调用:直接调用就可以, 不需要考虑第一个参数
- 对象调用:直接调用就可以
>>> class Person:
@staticmethod
def staticfunc():
print('静态方法')
>>> Person.staticfunc()
静态方法
>>> p = Person()
>>> p.staticfunc()
静态方法
4.5 不同类型的方法访问不同类型的属性
>>> class Person:
age = 18
def func(self): #可以访问类属性和实例属性
print(self.age)
print(self.num)
@classmethod
def classfunc(cls): #只能访问类属性
print(cls.age)
@staticmethod
def staticfunc(): #只能访问类属性
print(Person.age)
>>> p = Person()
>>> p.num = 22
>>> p.func()
18
22
>>> Person.classfunc()
18
>>> Person.staticfunc()
18
总结
-
在计算机编程中,对象是指具有状态和行为的实体。它是面向对象编程(OOP)的核心概念之一。面向过程编程和面向对象编程是两种不同的编程范式。在面向过程编程中,程序的结构是以函数为中心的,而在面向对象编程中,程序的结构是以对象为中心的。
-
类是对象的模板或蓝图,它定义了对象的属性和方法。属性是对象的特征,它们描述了对象的状态。属性可以是对象属性或类属性。对象属性是每个对象独有的,而类属性是所有对象共享的。
-
对象属性和变量之间有一些区别。变量是程序中用来存储数据的容器,而对象属性是对象的一部分,用于描述对象的状态。对象属性可以通过点运算符来访问,而变量可以直接使用。
-
对象属性可以通过定义类的构造函数来初始化。构造函数是一个特殊的方法,用于创建和初始化对象。类属性是在类定义中直接声明的属性,它们是所有对象共享的。
-
对象属性和类属性之间有一些区别。对象属性是每个对象独有的,而类属性是所有对象共享的。当一个对象的属性发生改变时,它只会影响该对象本身,而不会影响其他对象。而当一个类的属性发生改变时,所有对象的该属性都会发生改变。
-
有时候,我们希望限制对象属性的添加,以防止随意添加新属性。在Python中,可以使用
__slots__
属性来限制对象属性的添加。__slots__
属性是一个特殊的属性,它定义了一个对象可以拥有的属性的列表。 -
方法是对象可以执行的操作。它们定义了对象的行为。方法可以是实例方法、类方法或静态方法。实例方法是对象的方法,它可以访问对象的属性。类方法是与类相关联的方法,它可以访问类属性。静态方法是与类和对象无关的方法,它不能访问类属性或对象属性。
-
不同类型的方法可以访问不同类型的属性。实例方法可以访问对象属性和类属性。类方法可以访问类属性,但不能访问对象属性。静态方法不能访问类属性或对象属性。
总结来说,对象是具有状态和行为的实体,面向过程编程和面向对象编程是两种不同的编程范式。类是对象的模板,它定义了对象的属性和方法。属性是对象的特征,它们可以是对象属性或类属性。方法是对象可以执行的操作,它们可以是实例方法、类方法或静态方法。不同类型的方法可以访问不同类型的属性。
参考文献
[1]https://www.bilibili.com/video/BV1A4411v7b2
[2]https://blog.csdn.net/mall_lucy/article/details/106756318
—— END ——
如果以上内容有任何错误或者不准确的地方,欢迎在下面 👇 留言。或者你有更好的想法,欢迎一起交流学习~~~
更多精彩内容请前往 AXYZdong的博客