derezzed

导航

python面向对象之@staticmethod@classmethod

*小栗子助理解*

'''

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
'''

 

posted on 2018-01-22 15:52  derezzed  阅读(281)  评论(0)    收藏  举报