面向对象的编程语言?
Python到底是一种什么类型的编程语言:面向对象、函数式还是过程式语言?
Python借用了所有这3种流行方法的编程范式,还鼓励程序员根据需要混合使用。
在其他编程语言中(如Java)你写的所有代码都必须放在一个类里,要从这个类实例化对象
在Python中,"一切都是对象“不代表”一切都必须是类“。可以用你熟悉的任何一种范式创建代码,可以同样把所有代码放在一个类中,也可以不这么做
Python支持面向对象编程(OOP):如封装一个对象、继承和多态
Python的类
- 类将行为(本质即函数)和状态(本质即变量)打包在一个对象中
- Python中的类相当于JS中的原型,类可以完成对象实例化(从类可以创建新的对象实例)
- 对象共享行为,但不共享状态
从一个类创建对象时,每个对象会共享这个类的行为(类中定义的方法),但是会维护其自己的状态副本(属性)
为类增加方法:直接在类中定义函数即可
为类增加属性:在__init__
中完成
定义一个类,并创建对象
定义一个
CountFromBy
类,从这个类能创建如下的对象:
- 属性:具有初始值
val
和步长incr
(若没有指定,默认val为0,incr为1)- 方法:具有
increase()
方法,每次调用时对象的属性val增加incr
1. 为类增加方法
- 要为类增加方法,直接在类中使用
def
定义函数即可 - 注意,Python类的任何方法,必须传入第一个参数,用来指示当前对象,即当前正在使用此方法的对象
Python编程社区约定的做法是:总是将方法的第一个参数命名为self
(类似于JS中的this
) - 在方法代码组中,引用属性时要加
self
前缀,确保操作应用于对象的属性。
如下,应该写self.val
而不是val
前面说过对象共享行为,但不共享状态,只有这样才知道在方法中要修改哪一个对象的属性(即当前正在使用方法的那个对象)
另外,这样做能保证调用结束后,变量仍存在:如果只写val
,则调用结束后变量被销毁,无法再访问val(超出作用域),而对象中的val属性没有发生改变
class CountFromBy(object):#约定:对于类,采用CamelCase命名
def increase(self) -> None:
self.val += self.incr
...
...
注意,也可写成class CountFromBy:
或class CountFromBy():
(二者等价),但是建议写成class CountFromBy(object):
,作用是继承object,从而继承其一系列方法
如何调用方法
根据上文,已经知道Python对象调用方法时,第一个参数一定传入这个对象的引用
那么,对于CountFromBy类的a实例,我们调用increase方法时,应该这样写:
>>>a = CountFromBy(0,1)
>>>CountFromBy.increase(a)#调用类的一个方法,传入的第一个参数是某个对象实例
然而实际的写法为
>>>a = CountFromBy(0,1)
>>>a.increase()
实际上,解释器会自动将a.increase()
语句转化为CountFromBy.increase(a)
语句
因此我们只要使用a.increase()
的简单版本就好
- 这时要明白,我们调用
increase
方法时,需要提供的参数就少一个
因为在调用方法时,解释器自动将当前对象a
赋给第一个参数self
2. 从类创建对象实例时,初始化对象的属性
创建一个新对象时,如何初始化呢?
如果你了解面向对象编程,肯定会想到在其他语言中的“构造函数”。
构造函数是一个特殊方法,它定义了第一次创建一个对象时会发生什么(包括对象实例化和属性初始化)
在JS中使用构造函数:
//Dog称为构造函数
function Dog(name, breed, weight) {
//“对象实例化”部分(将传入的参数赋给新对象的属性)
this.name = name;
this.breed = breed;
this.weight = weight;
}
Dog.prototype.run = function() {
console.log("Run!");
};
//“属性初始化”部分
Dog.prototype.name = "dog";
Dog.prototype.breed = "unknown";
Dog.prototype.weight= 0;
//传入参数,获得一个具有某些属性的对象
var fido = new Dog("Fido", "Mixed", 38);
在 Python中有所不同:
- 对象实例化由解释器自动处理,所以不需要定义构造函数来完成这个工作
- 属性初始化的工作需要自己完成:有一个名为
__init__
(称为dunder init)的魔法方法允许你根据需要初始化属性。
绕路介绍一下前置知识:魔法方法__init__
(dunder init)
-
object
类是Python解释器内置的类 - Python中所有类都自动继承这个类(包括你自定义的类)
这意味着,所有类都继承了object
类的标准行为(即一些方法),当然也可以根据需要覆盖它们(提供你自己的方法实现) -
object
中的标准方法中,若其名称包含双下划线dunder__
,称为魔法方法
在自己的类中,覆盖这些魔法方法(编写自己的代码)通常很有用
覆盖__eq__
方法,可指定从类创建的对象使用相等操作符==
时会发生什么
覆盖__ge__
方法,可指定对对象使用大于操作符>
时要发生什么
覆盖__init__
方法,可指定要如何初始化对象属性
覆盖__repr__
方法,可指定解释器要如何表示对象
使用__init__
初始化对象的属性
从 Python类创建的新对象,如何初始化呢?
在你的类中覆盖(重新定义)__init__
方法即可
- 每次从你的类创建一个新对象时,解释器都会调用你的
__init__
方法 - 传入类的参数值,会被发送到
__init__
中
例如,对于mycount=CountFromBy(100,10)
,100和10会被传入__init__
ps.__init__
同样是一个方法,也要遵循前述规则:必须有第一个参数self
class CountFromBy:
def __init__(self,v: int=0, i: int=1) -> None:
#v默认值为0,i默认值为1
self.val = v
self.incr = i
def increase(self) -> None:
self.val += self.incr
3. 创建并使用对象
已经为类增加了方法、初始化了属性
下面就可以从类创建对象了
>>> a=CountFromBy()#使用默认值val=0,incr=1
>>> a.increase()
>>> a.val
1
>>> b=CountFromBy(100,10)#指定属性val=100,incr=10
>>> b.increase()
>>> b.val
110
额外介绍一个魔法方法:__repr__
直接打印“对象”,会显示什么呢?
>>> c=CountFromBy()
>>> c
<__main__.CountFromBy object at 0x000002BE66000A90>
>>> type(c)
<class '__main__.CountFromBy'>
>>> hex(id(c))
'0x2be66000a90'
解释器默认表示对象的方式是:对象类型+内存地址
编写自己的魔法方法__repr__
,可以覆盖这种默认行为,并指定解释器对于对象的字符串表示
class CountFromBy:
def __init__(self,v: int=0, i: int=1) -> None:
self.val = v
self.incr = i
def increase(self) -> None:
self.val += self.incr
def __repr__(self) -> str:
return str(self.val)
================= RESTART: C:\Users\13272\Desktop\CountFb.py =================
>>> c=CountFromBy(100,10)
>>> c.val
100
>>> c
100#自定义的对象表示方法
关于类的更多技巧
-
@staticmethod
:这个修饰符允许你在一个类中创建一个静态函数(不接收self作为第一个参数) -
@classmethold
:这个修饰符允许你创建一个类方法,它接收一个类作为它的第一个对象(通常称为cls),而不是self -
@property
:这个修饰符允许你重新指定和使用一个方法,就好像它是一个属性。 -
__slots__
:这是一个类指令,(使用时)可以大大提高从类创建对象的内存效率(要以牺牲一定的灵活性为代价)。