#第1章/lei.py
class Car():
# 方法
def getCarInfo(self):
print('车轮子个数:%d, 颜色%s'%(self.wheelNum, self.color))
def move(self):
print("车正在移动...")
在类中,可以定义所使用的方法,类中的方法与普通的函数只有一个特别的区别:它们必须有一个额外的第一个参数名称, 按照惯例它的名称是self。它表示类创建的实例本身,指向当前创建对象的内存地址。某个对象调用其方法时,Python解释器会把这个对象作为第一个参数传递给self,所以开发者只需要传递后面的参数即可。
#第1章/lei.py
# 定义类
class Car():
# 定义移动方法
def move(self):
print('车在行驶...')
# 定义鸣笛方法
def toot(self):
print("车在嘟嘟..")
# 创建一个对象,并用变量Wuling来保存它的引用
Wuling = Car()
Wuling.color = 'white' #使用‘.’符号的方法添加类属性:车的颜色
Wuling.wheelNum = 4 #使用‘.’符号的方法添加类属性:轮子数量
Wuling.move() #使用‘.函数名()’的语法调用类中函数:车的行驶
print(Wuling.color) #打印实例五菱的颜色属性
print(Wuling.wheelNum) #打印实例五菱的车轮数量属性
车在行驶... white 4
在上面的实例中,我们给五菱神车添加了两个对象属性:wheelNum和color,试想一下,如果再次创建一个对象的话,创建之后需要重新进行属性添加,这样做是很麻烦的。那么是否可以在创建对象时,就顺便把属性也给予了呢? 这就是init()函数的作用。代码如下:
#第1章/lei.py
# 定义汽车类
class Car():
# 初始化函数,对象属性有默认值:4和white;也可以通过传参的方法对对象属性进行重新赋值
def __init__(self, wheelNum=4, color='white'):
self.wheelNum = wheelNum
self.color = color
# 定义类方法
def move(self):
print('车在行驶')
# 创建对象五菱神车,不传参时,属性使用默认值
Wuling = Car()
print('五菱车的颜色为:', Wuling.color)
print('五菱车轮胎数量为:', Wuling.wheelNum)
# 创建对象自行车,传参时,新的传参值代替默认值
Bicycle = Car(2, 'black')
print('自行车的颜色为:', Bicycle.color)
print('自行车轮胎数量为:', Bicycle.wheelNum)
五菱车的颜色为: white 五菱车轮胎数量为: 4 自行车的颜色为: black 自行车轮胎数量为: 2
注意:
(1)当创建“Wuling对象”后,在没有调用方法的前提下,Wuling就默认拥有了2个属性:wheelNum和color,原因是方法是在创建对象后,就立刻被默认调用了init()函数,不需要手动调用。
(2)init(self)中,默认有1个参数名字为self,如果在创建对象时传递了2个实参,那么除了self作为第一个形参外还需要2个形参,例如init(self, x, y)。
(3)init(self)中的self参数,不需要开发者传递,Python解释器会自动把当前的对象引用传递进去。
魔法方法是在Python的类中被双下划线前后包围的方法。这些方法在类或对象进行特定的操作时会自动被调用,读者可以使用或重写这些魔法方法,给自定义的类添加各种特殊的功能来满足自己的需求。3.5.2节已经介绍过初始化魔法方法init,也是在定义类时最常见的魔法方法。除此之外,还有一些常见的魔法方法如下。
我们都知道一个最基本的魔术方法,init。通过此方法可以定义一个对象的初始操作。但init并不是第一个被调用的方法。
实际上,还有new方法,来实例化这个对象。然后,在创建时给初始化函数传递参数。在对象生命周期的另一端,也有del方法。
接下来看一看这三个方法。
new(cls, […])是在一个对象实例化的时候所调用的第一个方法,所以它才是真正意义上的构造方法。它的第一个参数是这个类,其他的参数是用来直接传递给init方法。
new 决定是否要使用该init方法,因为new可以调用其他类的构造方法或者直接返回别的实例对象来作为本类的实例,如果new没有返回实例对象,则init不会被调用。代码如下:
#第1章/lei.py
class Person(object):
def __new__(cls, *args, **kwargs):
print("__new__()方法被调用了")
print('这个是*agrs', *args)
print('这个是kwagrs', **kwargs)
# cls表示这个类,剩余所有的参数传给__init__()方法,
# 若不返回,则__init__()不会被调用
#return object.__new__(cls)
def __init__(self, name, age):
print("__init__()方法被调用了")
self.name = name
self.age = age
print(self.name, self.age)
p = Person("张三", 20)
__new__()方法被调用了 这个是*agrs 张三 20 这个是kwagrs
那new()方法在什么场景使用呢? 当我们需要继承内置类时。例如,想要继承int、str、tuple,就无法使用init来初始化了,只能通过new来初始化数据。实际上,在日常的编写中,new()和del()这两个魔法函数一般都不常见,保持默认就好,经常编写的是init()函数。del()函数代码如下:
#第1章/lei.py
class Washer:
# 当一个实例被销毁时自动调用的方法。
def __del__(self):
"""
当删除对象时,解释器会自动调用del方法
"""
print('对象已删除!')
haier = Washer()
#第1章/lei.py
class People(object):
name = 'VenusAI' #公有的类属性
__age = 7 #私有的类属性
def __init__(self):
pass
p = People()
print(p.name) #正确
print(People.name) #正确
#print(p.__age) #错误,不能在类外通过实例对象访问私有的类属性
#print(People.__age) #错误,不能在类外通过类对象访问私有的类属性
print(p._People__age) #这种特殊的访问方法可以在类外访问私有的类属性
VenusAI VenusAI 7
注意:类属性是声明在类的内部,实例方法的外部的属性,即在class内,__init__(self)方法之前。
实例属性是从属于实例对象的属性,也称为“实例变量”,要点如下:
(1)实例属性一般在init()方法中通过如下代码定义“self.实例属性名 = 初始值”。
(2)在本类的其他实例方法中,也是通过self进行访问“self.实例属性名”。
(3)实例属性可修改、新增、删除。
需要注意的是,如果在类外修改类属性,必须通过类对象去引用然后进行修改。如果通过实例对象去引用修改,会产生一个同名的实例属性,这种方式修改的是实例属性,不会影响到类属性。当类属性与实例属性同名时,一个实例访问这个属性时实例属性会覆盖类属性,但类访问时不会。代码如下:
#第1章/lei.py
class People(object):
country = 'China' #类属性
# 访问类属性
print(People.country)
# 实例化对象
p = People()
# 访问实例属性
print(p.country)
# 修改实例属性
p.country = 'Japan'
# 访问实例属性,实例属性会屏蔽掉同名的类属性
print(p.country)
# 访问类属性,会发现没有改变
print(People.country)
#通过类对象去引用修改类属性
People.country = "UK"
# 访问类属性
print(People.country)
China China Japan China UK
之前的例子中,在类中以def关键字定义的都可以称之为实例方法,不仅如此,类的初始化方法init()理论上也属于实例方法,只不过它比较特殊。实例方法最大的特点就是,它最少也要包含一个self参数,用于绑定调用此方法的实例对象“Python 会自动完成绑定”。实例方法通常会用类对象直接调用。
Python类方法和实例方法相似,它最少也要包含一个参数,只不过类方法中通常将其命名为cls,类方法是类对象所拥有的方法,需要用修饰器@classmethod来标识其为类方法,对于类方法,第一个参数必须是类对象,一般以cls作为第一个参数,可以通过实例对象和类对象去访问。类方法还有一个用途就是可以对类属性进行修改。代码如下:
#第1章/lei.py
class People(object):
country = 'China'
#类方法,用classmethod来进行修饰
@classmethod
def getCountry(cls):
return cls.country
@classmethod
def setCountry(cls,country):
cls.country = country
p = People()
print(p.getCountry()) #可以通过实例对象引用
print(People.getCountry()) #可以通过类对象引用
p.setCountry('Japan')
print(p.getCountry())
print(People.getCountry())
'''
China
China
Japan
Japan
'''
China China Japan Japan
'\nChina\nChina\nJapan\nJapan\n'
类方法什么时候使用呢?我们可以考虑一个场景。
假设有一个学生类和一个班级类,需实现的功能为:学生类继承自班级类,每实例化一个学生,班级人数都会增加。最后,需要实例化一些学生,并获取班级中的总人数。
思考:这个问题用类方法做比较合适,为什么?
我们要实例化的是学生,但是从学生实例中获取班级总人数,在逻辑上显然是不合理的。如果要获得班级总人数,生成一个班级实例是没有必要的。因此,编写一个类方法最为合适,这个方法能够访问并更新班级的总人数,而不需要创建班级实例。
静态方法需要通过修饰器@staticmethod来进行修饰。静态方法是类中的函数。静态方法主要是用来存放逻辑性的代码,逻辑上属于类,但是和类本身没有关系,也就是说在静态方法中,不会涉及类中的属性和方法的操作。可以理解为,静态方法是个独立的、单纯的函数,它仅仅托管于某个类的名称空间中,便于使用和维护。例如,笔者想定义一个关于时间操作的类,其中有一个获取当前时间的函数,代码如下:
#第1章/lei.py
import time
class TimeTest(object):
def __init__(self, hour, minute, second):
self.hour = hour
self.minute = minute
self.second = second
@staticmethod
def showTime():
return time.strftime("%H:%M:%S", time.localtime())
# 使用类对象调用静态方法
print(TimeTest.showTime())
# 实例化对象
t = TimeTest(2, 10, 10)
# 使用实例对象调用静态方法
print(t.showTime())
14:22:56 14:22:56