*小栗子助理解*
'''
class Num:
#普通方法:能用Num调用而不能用实例化对象调用
def one():
print ('1')
#实例方法:能用实例化对象调用而不能用Num调用
def two(self):
print ('2')
#静态方法:能用Num和实例化对象调用
@staticmethod
def three():
print ('3')
#类方法:第一个参数cls长什么样不重要,都是指Num类本身,调用时将Num类作为对象隐式地传入方法
@classmethod
def go(cls):
cls.three()
Num.one() #1
#Num.two() #TypeError: two() missing 1 required positional argument: 'self'
Num.three() #3
Num.go() #3
i=Num()
#i.one() #TypeError: one() takes 0 positional arguments but 1 was given
i.two() #2
i.three() #3
i.go() #3
'''
@classmethod
# python @classmethod 的使用场合
'''
官方的说法:
classmethod(function)
中文说明:
classmethod是用来指定一个类的方法为类方法,没有此参数指定的类的方法为实例方法,使用方法如下:
'''
# class C:
# @classmethod
# def f(cls, arg1, arg2, ...): ...
'''
看后之后真是一头雾水。说的啥子东西呢???
自己到国外的论坛看其他的例子和解释,顿时就很明朗。 下面自己用例子来说明。
看下面的定义的一个时间类:
'''
# class Data_test(object):
# day=0
# month=0
# year=0
# def __init__(self,year=0,month=0,day=0):
# self.day=day
# self.month=month
# self.year=year
#
# def out_date(self):
# print "year :"
# print self.year
# print "month :"
# print self.month
# print "day :"
# print self.day
# t=Data_test(2016,8,1)
# t.out_date()
# 输出:
#
# year :
# 2016
# month :
# 8
# day :
# 1
'''
符合期望。
如果用户输入的是
"2016-8-1"
这样的字符格式,那么就需要调用Date_test
类前做一下处理:
'''
# string_date='2016-8-1'
# year,month,day=map(int,string_date.split('-'))
# s=Data_test(year,month,day)
'''
先把‘2016-8-1’ 分解成 year,month,day 三个变量,然后转成int,再调用Date_test(year,month,day)函数。 也很符合期望。
那我可不可以把这个字符串处理的函数放到 Date_test 类当中呢?
那么@classmethod 就开始出场了
'''
# class Data_test2(object):
# day=0
# month=0
# year=0
# def __init__(self,year=0,month=0,day=0):
# self.day=day
# self.month=month
# self.year=year
#
# @classmethod
# def get_date(cls,string_date):
# #这里第一个参数是cls, 表示调用当前的类名
# year,month,day=map(int,string_date.split('-'))
# date1=cls(year,month,day)
# #返回的是一个初始化后的类
# return date1
#
# def out_date(self):
# print "year :"
# print self.year
# print "month :"
# print self.month
# print "day :"
# print self.day
'''
在Date_test类里面创建一个成员函数, 前面用了 @ classmethod装饰。 它的作用就是有点像静态类,
比静态类不一样的就是它可以传进来一个当前类作为第一个参数。
那么如何调用呢?
'''
# r=Data_test2.get_date("2016-8-6")
# r.out_date()
# 输出:
#
# year :
# 2016
# month :
# 8
# day :
# 1
'''
这样子等于先调用get_date()对字符串进行处理,然后才使用Data_test的构造函数初始化。
这样的好处就是你以后重构类的时候不必要修改构造函数,只需要额外添加你要处理的函数,然后使用装饰符 @classmethod 就可以了。
'''
@classmethod与@staticmethod的区别
# Python面向对象编程中,类中定义的方法可以是 @classmethod 装饰的类方法,
# 也可以是 @staticmethod 装饰的静态方法,用的最多的还是不带装饰器的实例方法,
# 如果把这几个方法放一块,对初学者来说无疑是一头雾水,那我们该如何正确地使用它们呢?
#
# 先来看一个简单示例:
'''
class A(object):
def m1(self, n):
print("self:", self)
@classmethod
def m2(cls, n):
print("cls:", cls)
@staticmethod
def m3(n):
pass
a = A()
a.m1(1) # self: <__main__.A object at 0x000001E596E41A90>
A.m2(1) # cls: <class '__main__.A'>
A.m3(1)
'''
# 我在类中一共定义了3个方法,m1 是实例方法,第一个参数必须是 self(约定俗成的)。
# m2 是类方法,第一个参数必须是cls(同样是约定俗成),m3 是静态方法,参数根据业务需求定,可有可无。
# 当程序运行时,大概发生了这么几件事(结合下面的图来看)。
# 第一步:代码从第一行开始执行class 命令,此时会创建一个类 A 对象(没错,类也是对象,一切皆对象嘛)
# 同时初始化类里面的属性和方法,
# 记住,此刻实例对象还没创建出来。
#
#
# 第二、三步:接着执行
# a = A(),系统自动调用类的构造器,构造出实例对象a
#
# 第四步:接着调用
# a.m1(1) ,m1
# 是实例方法,内部会自动把实例对象传递给self
# 参数进行绑定,也就是说, self和a指向的都是同一个实例对象。
#
# 第五步:调用A.m2(1)
# 时,python内部隐式地把类对象传递给cls参数,cls和A都指向类对象。
![]()
# 严格意义上来说,左边的都是变量名,是对象的引用,右边才是真正的对像,
# 为了描述方便,我直接把 a 称为对象,
# 你应该明白我说对象其实是它所引用右边的那个真正的对象。
# 再来看看每个方法各有什么特性
# ********************************************************************************
# 实例方法
'''
print(A.m1)
# A.m1在py2中显示为<unbound method A.m1>
<function A.m1 at 0x000002BF7FF9A488>
print(a.m1)
<bound method A.m1 of <__main__.A object at 0x000002BF7FFA2BE0>>
'''
# A.m1是一个还没有绑定实例对象的方法,对于未绑定方法,调用 A.m1 时必须显示地传入一个实例对象进去,
# 而 a.m1是已经绑定了实例的方法,
# python隐式地把对象传递给了self参数,所以不再手动传递参数,这是调用实例方法的过程。
'''
A.m1(a, 1)
# 等价
a.m1(1)
'''
# 如果未绑定的方法 A.m1 不传实例对象给 self 时,
# 就会报参数缺失错误,在 py3 与 py2 中,两者报的错误不一致,
# python2 要求第一个参数self是实例对象,而python3中可以是任意对象。
'''
A.m1(1)
TypeError: m1() missing 1 required positional argument: 'n'
'''
# ********************************************************************************
# 类方法
'''
print(A.m2)
<bound method A.m2 of <class '__main__.A'>>
print(a.m2)
<bound method A.m2 of <class '__main__.A'>>
'''
# m2是类方法,不管是 A.m2 还是 a.m2,都是已经自动绑定了类对象A的方法,
# 对于后者,因为python可以通过实例对象a找到它所属的类是A,找到A之后自动绑定到 cls。
'''
A.m2(1)
# 等价
a.m2(1)
'''
# 这使得我们可以在实例方法中通过使用 self.m2()这种方式来调用类方法和静态方法。
'''
def m1(self, n):
print("self:", self)
self.m2(n)
'''
# ********************************************************************************
# 静态方法
'''
print(A.m3)
<function A.m3 at 0x000002BF7FF9A840>
print(a.m3)
<function A.m3 at 0x000002BF7FF9A840>
'''
# m3是类里面的一个静态方法,跟普通函数没什么区别,与类和实例都没有所谓的绑定关系,
# 它只不过是碰巧存在类中的一个函数而已。不论是通过类还是实例都可以引用该方法。
'''
A.m3(1)
# 等价
a.m3(1)
'''
# 以上就是几个方法的基本介绍。
# 现在把几个基本的概念理清楚了,那么现在来说说几个方法之间的使用场景以及他们之间的优缺点。
# ********************************************************************************
# 应用场景
# 静态方法的使用场景:
# 如果在方法中不需要访问任何实例方法和属性,纯粹地通过传入参数并返回数据的功能性方法,
# 那么它就适合用静态方法来定义,它节省了实例化对象的开销成本,
# 往往这种方法放在类外面的模块层作为一个函数存在也是没问题的,而放在类中,仅为这个类服务。
#
# 例如下面是微信公众号开发中验证微信签名的一个例子,它没有引用任何类或者实例相关的属性和方法。
'''
from hashlib import sha1
import tornado.web
class SignatureHandler(tornado.web.RequestHandler):
def get(self):
"""
根据签名判断请求是否来自微信
"""
if self._check_sign(TOKEN, timestamp, nonce, signature):
self.write(echostr)
else:
self.write("你不是微信发过来的请求")
@staticmethod
def _check_sign(token, timestamp, nonce, signature):
sign = [token, timestamp, nonce]
sign.sort()
sign = "".join(sign)
sign = sha1(sign).hexdigest()
return sign == signature
'''
# 类方法的使用场景有:
#
# 作为工厂方法创建实例对象,例如内置模块 datetime.date 类中就有大量使用类方法作为工厂方法,
# 以此来创建date对象。
'''
class date:
def __new__(cls, year, month=None, day=None):
self = object.__new__(cls)
self._year = year
self._month = month
self._day = day
return self
@classmethod
def fromtimestamp(cls, t):
y, m, d, * = _time.localtime(t)
return cls(y, m, d)
@classmethod
def today(cls):
t = _time.time()
return cls.fromtimestamp(t)
'''
# 如果希望在方法裡面调用静态类,那么把方法定义成类方法是合适的,
# 因为要是定义成静态方法,那么你就要显示地引用类A,这对继承来说可不是一件好事情。
'''
class A:
@staticmethod
def m1()
pass
@staticmethod
def m2():
A.m1() # bad
@classmethod
def m3(cls):
cls.m1() # good
'''