1. __init__
Python在你实例化class类的时候会检查这个类中是否有init,如果有就会调用它。进行初始化。init()方法意义重大的原因有两个:
- 在对象生命周期中初始化;每个对象必须正确初始化后才能正常工作。
- _init__()参数值可以有多种形式。
class song:
def __init__(self,lyrics):
self.ly = lyrics #空对象中把传来的lyrics,赋值给一个叫ly变量
print(type(self.ly))
def sing(self):#这里的self可以随便改成什么别的名字(se),下面也跟着改了就好(se.ly)。可以自己试一试,但是不能没有
for line in self.ly: #这里的ly名字不能改,改了(比如说改成sa)就提示说song这个类没有sa这个属性
print(line)
def sng(sb):
print(sb.ly) #这里的sb.ly还是__init__里头self.ly的值
a = song(['A','B','C']) self指向a,即self就是a,self. = a.
b = song(['D','E','F'])
a.sing()
b.sing()
a.sng()
因为有很多种方式为init()提供参数值,对于对象创建有大量的用例,我们可以看看其中的几个。我们想尽可能的弄清楚,因此我们需要定义一个初始化来正确的描述问题区域。
2. __name__
name 是属于 python 中的内置类属性,就是它会天生就存在于一个 python 程序中,代表对应程序名称。
比如所示的一段代码里面(这个脚本命名为 pcRequests.py),我只设了一个函数,但是并没有地方运行它,所以当 run 了这一段代码之后我们有会发现这个函数并没有被调用。但是当我们在运行这个代码时这个代码的 name 的值为 main (一段程序作为主线运行程序时其内置名称就是 main)。
import requests
class requests(object):
def __init__(self,url):
self.url=url
self.result=self.getHTMLText(self.url)
def getHTMLText(url):
try:
r=requests.get(url,timeout=30)
r.raise_for_status()
r.encoding=r.apparent_encoding
return r.text
except:
return "This is a error."
print(__name__)
结果:
__main__
Process finished with exit code 0
当这个 pcRequests.py 作为模块被调用时,则它的 name 就是它自己的名字:
import pcRequestspcRequestsc=pcRequestsc.__name__
结果:
'pcRequests'
看到这里应该能明白,自己的 name 在自己用时就是 main,当自己作为模块被调用时就是自己的名字,就相当于:我管自己叫我自己,但是在朋友眼里我就是小仙女一样。
3. __new__
- new方法默认返回实例对象供init方法、实例方法使用。
- init方法为初始化方法,为类的实例提供一些属性或完成一些动作。
- new方法创建实例对象供init方法使用,init方法定制实例对象。
new方法必须返回值,init方法不需要返回值。(如果返回非None值就报错) - 对象的创建和初始化过程示意图
- 执行流程:
- 创建Person类的对象student=Person(‘kb’,19),先执行等号右边的内容Person(‘kb’,19)
- 调用Person类中的new方法,并将Person类传给方法中的cls
- 创建obj对象
- 执行init方法,并将obj对象赋值给self
- 将self再传给student1对象
- 我们比较两个方法的参数,可以发现new方法是传入类(cls),而init方法传入类的实例化对象(self),而有意思的是,new方法返回的值就是一个实例化对象(ps:如果new方法返回None,则init方法不会被执行,并且返回值只能调用父类中的new方法,而不能调用毫无关系的类的new方法)。
我们可以这么理解它们之间的关系,new是开辟疆域的大将军,而init是在这片疆域上辛勤劳作的小老百姓,只有new执行完后,开辟好疆域后,init才能工作,结合到代码,也就是new的返回值正是init中self。
4. __call__
4.1. 使用示例
例类Run中展示call的使用方法,call接收对象传入的参数,可在call方法里执行某一特定对象的参数和方法(pytorch中模型正向传播的forward应该是对该函数的重写):
class Run():
def __init__(self,a):
print('这是a:',a)
def __call__(self, *args, **kwargs):
print("这是call里的其他参数",args)
test=Run(123)
test('hehe')
输出:
这是a: 123
这是call里的其他参数: ('hehe',)
对象实例w的两种等价调用方式分别是Run.call(x)和Run(x),前者为普通的调用类方法的方式,后者为Python中的语法实现了将对象变为可调用的目的。
4.2. 类可调用的与对象可调用的不同
要注意类都是可调用的,而对象可通过call()函数来设计为可调用或者不可调用。如下代码
class WaterMelon:
def __init__(self):
self.price = 0
self.price_per_kilo = 1.0
def __call__(self,weight):
self.price = self.price_per_kilo * weight
print("The pricce of WaterMelon is: ", self.price)
class Apple:
def __init__(self):
self:price_per_kilo = 3.0
w = WaterMelon()
w(6)
#The pricce of WaterMelon is: 6
w.__call__(7)
#The pricce of WaterMelon is: 7
callable(WaterMelon)
#True
callable(Apple)
#True
callable(w)
#True
callable(a)
#False
这里Apple类没有定义call()方法,因此其对象实例不是可调用对象,但是类本身是可调用的,因为调用类实际上就是返回一个实例,类似于执行init()函数。
4.3. call()函数还有其它用途
Python可以进行元编程。有一个技巧就是,当你不想让用户能够实例化一个类,而只能使用类的静态方法的时候,你可以通过结合call()函数与元编程来实现:
class NoInstances(type):
def __call__(self, *args, **kwargs):
raise TypeError("Class is not allowed to be instantiated")
class Cherry(metaclass=NoInstances):
@staticmethod
def about():
print("樱桃")
现在c = Cherry()
会报错,而c = Cherry().about()
可正常执行
5. __getitem__
例类Tag,自定义的对象如果没有迭代器 iter 、next 迭代器协议,使用[]运算符取对象列表或者字典中的值时,会自动调用它的 getitem 方法;可重定义此方法,使对象通过类似列表或者字典的调用方式实现迭代取值:
class Tag:
def __init__(self,id):
self.list=[]
self.id=id
def __getitem__(self, item):
self.list=[]
for i in range(self.id):
self.list.append('这是'+str(i))
print('这个方法被调用')
return self.list[item]
a=Tag(12)
print(a.list)
print(a[1])
print(a[2])
print(a[9])
print(a[4])
print(a.list)
输出:
[]
这是1
这个方法被调用
这是2
这个方法被调用
这是9
这个方法被调用
这是4
这个方法被调用
['这是0','这是1','这是2','这是3','这是4','这是5','这是6','这是7','这是8','这是9','这是10','这是11']
6. enter和exit
为了支持with语句,可以在类中定义enter和exit函数
class ceshi():
def __init__(self):
pass
def __enter__(self):
print('进入with语句')
return self
def __exit__(self,*args):
print('退出with')
with ceshi() as c:
print(c)
输出:
进入with语句
<__main__.ceshi object at 0x000001ED96D6BE88>
退出with