您的位置:时时app平台注册网站 > 编程知识 > Python基础(七) python自带的三个装饰器时时app平台

Python基础(七) python自带的三个装饰器时时app平台

2019-10-12 09:17

1)是把函数嵌入到类中的一种方式,函数就属于类,同时表明函数不需要访问这个类

3、python 方法解析顺序

classmethod:把类中的方法定义为类方法,使用classmethod装饰的方法可以使用类或者类的实例对象来调用,并将该class对象隐式的作为第一个参数传入

普通函数方法:self做为第一个参数,隐式的将类实例传递给方法,调用方法时,类必须实例化。

0、元类

元类就是类的类,所体现的终极思想就是一切皆对象。

时时app平台注册网站 1

image.png

关于深层次,待使用到在总结。

@calc_spend_time就是语法糖,它的本质就是:calc_add = calc_spend_time(calc_add)

 @property 修饰,就是将方法,变成一个属性来使用。

__slots__ 用来限制属性
>>> class people(object):
...     __slots__ = ('age','name') # 用tuple定义允许绑定的属性名称
... 
>>> p = people()
>>> p.age = 20
>>> p.na = yuan
>>> p.na = 'yuan'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'people' object has no attribute 'na'
>>> p.name = 'yuan'
>>> 

简单来说,可以把装饰器理解为一个包装函数的函数,它一般将传入的函数或者是类做一定的处理,返回修改之后的对象.所以,我们能够在不修改原函数的基础上,在执行原函数前后执行别的代码.比较常用的场景有日志插入,事务处理等.

 

示例2:
def check_is_admin(f):
    def wrapper(*args, **kwargs):
        if kwargs.get('username') != 'admin':
            raise Exception("error occur")
        return f(*args, **kwargs)
    return wrapper

class store(object):
    @check_is_admin
    def get_food(self,username,food):
        print food

s = store()
s.get_food(username='admin',food='noodles')
print s.get_food.__name__

上述程序定义了check_is_admin的装饰器,装饰器的主要作用是调用某个函数之前执行一类通用的操作,比如日志任务,上述是执行了权限检查。
函数被装饰器修饰时,本质上函数变为
get_food = check_is_admin(get_food(self,username,food))
check_is_admin直接返回 wrapper函数地址,因此get_food也是指向wrapper函数,故print s.get_food.__name__结果是 wrapper.
因此调用s.get_food(username='admin',food='noodles')也就是
wrapper(username='admin',food='noodles')。该函数最后一定要有return f(*args, **kwargs) ,这确保原来函数被执行并返回结果。
因为装饰器使原函数指向了另一个函数(如上面的wrapper),而原函数只是该函数的一部分,该方式确实对原函数进行了扩展。但同时引入了另外的问题,原函数的属性和名字没有了,如上面s.get_food.__name__并不是get_food。functools提供了名为wraps的装饰器,会复制这些属性给装饰器函数,用法如下:

import functools
def check_is_admin(f):
    @functools.wraps(f)
    def wrapper(*args, **kwargs):
        if kwargs.get('username') != 'admin':
            raise Exception("error occur")
        #return f(*args, **kwargs)
    return wrapper

只需额外添加两行代码。
值得一提的是,**kwargs指定了字典方式传入数据,因此只支持s.get_food(username='admin',food='noodles')而不支持s.get_food('admin','noodles')。为了代码的通用性,考虑对其进行完善,使用inspect模块,最终为:

import functools
import inspect
def check_is_admin(f):
    @functools.wraps(f)
    def wrapper(*args, **kwargs):
        func_args = inspect.getcallargs(f,*args,**kwargs)
        if func_args.get('username') != 'admin':
            raise Exception("error occur")
        print 'test'
        return f(*args, **kwargs)
    return wrapper

func_args会以字典形式记录对应的key和value。意味着装饰器不用检查参数是否是基于位置的参数还是关键字参数,最终以相同的格式保存在返回字典中。

但是现在又有新的需求,计算求和操作耗时,很简单,求和前获取一下时间,求和后再获取一次,求差即可

 

4、python定制类和魔法方法

def calc_add(a, b):
 return a   b
calc_add(1, 2)
class A():
    def func(self,x,y):
        return x * y

    @classmethod
    def cfunc(cls,x,y):
        return x * y

if __name__=="__main__":
    print A().func(5,5)
    print A.cfunc(4,5)

5、__slots__

当定义一个类时,可以动态的给该类绑定一个属性和方法,比如:

>>> class Student(object):
...     pass

>>> s = Student()
>>> s.name = 'Michael' # 动态给实例绑定一个属性
>>> print s.name
Michael

>>> def set_age(self, age): # 定义一个函数作为实例方法
...     self.age = age
...
>>> from types import MethodType
>>> s.set_age = MethodType(set_age, s, Student) # 给实例绑定一个方法
>>> s.set_age(25) # 调用实例方法
>>> s.age # 测试结果
25

注意的是,上面是给一个实例绑定的相应的方法,也就是说当在生成一个实例时,上述增加的属性和方法就不起作用了。可以给class绑定方法:

>>> def set_score(self, score):
...     self.score = score
...
>>> Student.set_score = MethodType(set_score, None, Student)

只需将MethodType第二个参数改为None就行。

上面的例子就是装饰器的概念,包装函数的函数.事实上上面的例子还可以更精简

1、@property   将某函数,做为属性使用

6、@property使用

参考
http://python.jobbole.com/80955/
由上面一节可以知道,绑定属性时,可以随意修改属性值,比如

s = Student()
s.score = 9999

很多时候都需要对属性值进行判断,比如正负,大小范围等,一般来说就需要写一个函数进行逻辑检查,比如:

class Student(object):

    def get_score(self):
        return self._score

    def set_score(self, value):
        if not isinstance(value, int):
            raise ValueError('score must be an integer!')
        if value < 0 or value > 100:
            raise ValueError('score must between 0 ~ 100!')
        self._score = value

这样就能保证能对传入的值进行逻辑约束,但是每次设置需要调用相应函数,比如s.set_score( 99 ),又显得不是很简洁,能不能像 s.score = 99一样简洁又能进行逻辑检查呢。就是@property。
@property装饰器可以将一个method变为属性,可以像属性一样简单调用,如student.get_score ,若没有装饰器,则返回的是函数地址。关于setter用法见下。

class Student(object):

    @property
    def score(self):
        return self._score

    @score.setter
    def score(self, value):
        if not isinstance(value, int):
            raise ValueError('score must be an integer!')
        if value < 0 or value > 100:
            raise ValueError('score must between 0 ~ 100!')
        self._score = value

@score.setter装饰器表示可以对该属性赋值,若没有则是一个只读的属性。

property:把方法变成属性

说到装饰器,就不得不说python自带的三个装饰器:

__str__ 和 __rerp__
class yuan(object):
    def __init__(self):
        self.name = 'yuanqijie'
        self.age = 22
        self.ambition = 'yes'
    def __str__(self):
        return 'object name: %s'  % self.name

qijie = yuan()
print qijie

输出为:
object name: yuanqijie

若没有重写 __str__ 则输出为 <main.Student object at 0x109afb310>
但注意到,若直接输出变量而不是用print在提示符下还是上述信息,因为直接显示变量调用的不是str(),而是repr(),两者的区别是str()返回用户看到的字符串,而repr()返回程序开发者看到的字符串,也就是说,repr()是为调试服务的。可以类似上述方法进行重写,作为了解即可。

def calc_spend_time(func, *args, **kargs):
 start_time = datetime.datetime.now()
 result = func(*args, **kargs)
 end_tiem = datetime.datetime.now()
 print "result:", result, "used:", (end_tiem - start_time).microseconds, "μs"
def calc_add(a, b):
 return a   b
calc_spend_time(calc_add, 1, 1)
# calc_spend_time(calc_add, a=1, b=2)

带修饰类方法:cls做为方法的第一个参数,隐式的将类做为对象,传递给方法,调用时无须实例化。

1、python 对象和类的绑定以及类方法,静态方法

通常我们要使用一个类中的方法时,都需要实例化该类,再进行调用,那类中 self 和 cls 有什么含义,能不能不初始化一个实例而直接调用类方法,对象方法和类方法,静态方法又有什么关系。是本篇文章思考的问题。

类的调用有以下两种方法:

>>>class Test:
...    def func(self, message):
...        print message
...
>>>object1=Test()
>>>x=object1.func
>>>x('abc')
abc
>>>t=Test.func
>>>t(object1,'abc')
abc

但是对于 t=Test.func 来说,变量名 t 是关联到了类 Test 的func 方法的地址上,t是非绑定的,所以在调用t(object1, ‘abc’) 时,必须显式的将实例名与 self 关联,否则将会报出”TypeError: unbound method func() must be called with Test instance as first argument (got str instance instead)” 的错误。

现在呢,函数calc_diff(a, b),计算a-b,也想计算减法操作的时间差,很好办,把那段代码复制过去.但是假如我们现在想编的是一个数学函数库,各种函数都想计算其执行耗时,总不能一个一个复制代码,想个更好的办法.

3、@staticmethod  修饰类的方式

  • 元类
  • python 对象和类的绑定以及类方法,静态方法
  • python 子类调用父类方法总结
  • python 方法解析顺序MRQ
  • python定制类和魔法方法
  • 关于用法__slots__
  • @property使用
  • 修饰器
import datetime
def calc_add(a, b):
 start_time = datetime.datetime.now()
 result = a   b
 end_tiem = datetime.datetime.now()
 print "result:", result, "used:", (end_tiem - start_time).microseconds, "μs"
 return result
calc_add(1, 2)

不前进,不倒退,停止的状态是没有的.

参考学习

形如__xxx__的变量或者函数名要注意,这些在Python中是有特殊用途。常见的就是__inint__()函数了,在对象创建后用来初始化,类似的还有__new()__ 和__del__函数。用的不是很多,不做细节深入。

无参数的函数装饰器

class A():


    @property
    def pfunc(self):
        return self.value

    @pfunc.setter
    def pfunc(self,value):
        self.value = value

    @property
    def pfunc1(self):
        print('this is property')

if __name__=="__main__":

    A.pfunc = 9
    print A.pfunc
    A.pfunc1
示例1:
class myDecorator(object):
    def __init__(self, fn):
        print "inside myDecorator.__init__()"
        self.fn = fn

    def __call__(self):
        self.fn()
        print "inside myDecorator.__call__()"


@myDecorator
def aFunction():
    print "inside aFunction()"

print "Finished decorating aFunction()"
aFunction()

运行上述输出为:

inside myDecorator.__init__()
Finished decorating aFunction()
inside aFunction()
inside myDecorator.__call__()

看起来也不错,负责计算的函数不用更改,只需调用的时候作为参数传给计算时间差的函数.但就是这,调用的时候形式变了,不再是clac(1, 2),而是calc_spend_time(clac_add, 1, 2),万一calc_add大规模被调用,那么还得一处一处找,然后修改过来,还是很麻烦.如果想不修改代码,就得使clac()calc_spend_time(clac)效果一样,那么可以在calc_spend_time()里把传入的clac包装一下,然后返回包装后的新的函数,再把返回的包装好的函数赋给clac,那么calc()的效果就和上例calc_spend_time(calc())效果一样.

2、@classmethod  修饰类的方式

参考学习

明白以下几点:
1、类默认的方法都是绑定对象的,而self参数也是指向该对象,没有实例化对象时,类中方法调用会出错,也涉及到python自动传递self参数。
2、若想不实例化而直接通过 类名.方法 来调用,需要指定该方法绑定到类,如下,一要使用@classmethod 装饰器,二方法中第一个参数为cls,而不是self。

>>> class Foo(object):          
...     @classmethod                #定义类方法要点1
...     def foo(cls):               #定义类方法要点2
...             print 'call foo'
... 
>>> Foo.foo()
call foo
>>> Foo().foo()
call foo

类也是对象,因此和下面的静态方法还是有不一样。类中直接定义的属性如下,在类方法中也是可以直接使用的。

class pizza(object):
    radius = 42
    @classmethod
    def get_radius(cls):
        return cls.radius
print pizza.get_radius()

类方法对于创建工厂函数最有用,如下

class pizza(object):
    def __init__(self,ingre):
        self.ingre = ingre

    @classmethod
    def from_other(cls,fridge):
        return cls(fridge.juice() fridge.cheese())  
    def get_ingre(self):
        return self.ingre

cls代表该类,cls()也是用来创建对象,和pizza(fridge.juice() fridge.cheese())效果一样。待理解,工厂方法是什么?
3、若只想当成一个普通函数,定义不包含self和cls,则可以使用静态方法,如下:

>>> class Foo(object):
...     @staticmethod
...     def foo():
...             print 'call foo'
... 
>>> Foo.foo()
call foo
>>> Foo().foo()
call foo

作者:_Zhao_
链接:http://www.jianshu.com/p/4b871019ef96
來源:简书

您可能感兴趣的文章:

  • python如何定义带参数的装饰器
  • 介绍Python的@property装饰器的用法
  • Python中的各种装饰器详解
  • 深入理解python中的闭包和装饰器
  • Python装饰器的函数式编程详解
  • 详解Python中的装饰器、闭包和functools的教程
  • 巧用Python装饰器 免去调用父类构造函数的麻烦
  • Python中的多重装饰器
  • python重试装饰器示例
  • 实例讲解Python编程中@property装饰器的用法
  • Python自定义装饰器原理与用法实例分析

一起进步,与君共勉,

7、修饰器

参考学习

import datetime
def calc_spend_time(func):
 def new_func(a, b):
  start_time = datetime.datetime.now()
  result = func(a, b)
  end_tiem = datetime.datetime.now()
  print "result:", result, "used:", (end_tiem - start_time).microseconds, "μs"
 return new_func
@calc_spend_time
def calc_add(a, b):
 return a   b
calc_add(1, 2)

 

参考

上述博文具有很强的参考意义,转述如下:
在类的多继承中,方法解析顺序MRQ具有很重要的意义,比如对以下菱形继承,D的实例调用show方法,是调用A的还是C的show。

时时app平台注册网站 2

image.png

python解析顺序的规范化也是一个不断发展的过程,主要有以下两个阶段:

  • 2.2之前的经典类。经典类中多继承方法解析采用深度优先从左到右搜索,即D-B-A-C-A,也就是说经典类中只采用A的show方法。
  • 经典类对单层继承没有什么问题,但是对上述来说,我们显然更愿意使用C的show方法,因为他是对A的具体化,但是经典类比并不能实现,于是在2.2中引入新式类(继承自object),它仍然采用从左至右的深度优先遍历,但是如果遍历中出现重复的类,只保留最后一个。并且在定义类时就计算出该类的 MRO 并将其作为类的属性。因此新式类可以直接通过 mro 属性获取类的 MRO。
    举个例子:

时时app平台注册网站 3

image.png

按照深度遍历,其顺序为 [D, B, A, object, C, A, object],重复类只保留最后一个,因此变为 [D, B, C, A, object]

这样看起来好像么有问题,但是会有潜在的问题,比如破坏了单调性原则,因此在2.3中引入了 __ C3 算法__。

希望本文所述对大家Python程序设计有所帮助。

Linux and python学习交流3群新开,欢迎加入,一起学习.qq 3群:563227894

2、python 子类调用父类方法总结

参考来源

talk is weak,从程序开始:

class Person(object):
    def __init__(self):
        self.name = "Tom"
    def getName(self):
        return self.name

class Student(Person):
    def __init__(self):
        self.age = 12
    def getAge(self):
        return self.age

if __name__ == "__main__":
    stu = Student()
    print stu.getName()

作者:nummy
链接:http://www.jianshu.com/p/dfa189850651

时时app平台注册网站 4

image.png

上面只是说明一个常用的子类调用父类场景,即调用父类的初始化函数。
直接运行以上代码会出错,因为尽管Student类继承了Person类,但是并没有调用父类的init()方法,因为子类中对init函数进行了重写,若没有重写会直接继承父类的init函数自动运行。有以下两种方法:

参考
1、super方法

class Base:
    def __init__(self):
        print('Base.__init__')

class A(Base):
    def __init__(self):
        # super().__init__()
        super(A,self).__init__()
        print('A.__init__')

class B(Base):
    def __init__(self):
        # super().__init__()
        super(B,self).__init__()
        print('B.__init__')

class C(A,B):
    def __init__(self):
        # super().__init__()  # Only one call to super() here  python3
        super(C,self).__init__()
        print('C.__init__')

运行结果

>>> c = C()
Base.__init__
B.__init__
A.__init__
C.__init__
>>>

2、调用未绑定的父类构造方法

class Person(object):
    def __init__(self):
        self.name = "Tom"
    def getName(self):
        return self.name

class Student(Person):
    def __init__(self):
        Person.__init__(self)
        self.age = 12
    def getAge(self):
        return self.age

if __name__ == "__main__":
    stu = Student()
    print stu.getName()

作者:nummy
链接:http://www.jianshu.com/p/dfa189850651

非绑定方法不经常用到,上述场景却使用的比较多(即子类覆盖父类的方法)。运行时没有父类person的实例,需要显示地进行传递,但有Student的实例,可以用来进行代替。
这种方法叫做调用父类的未绑定的构造方法。在调用一个实例的方法时,该方法的self参数会被自动绑定到实例上(称为绑定方法)。但如果直接调用类的方法(比如Person.__init),那么就没有实例会被绑定。这样就可以自由的提供需要的self参数,这种方法称为未绑定unbound方法。
通过将当前的实例作为self参数提供给未绑定方法,Student类就能使用其父类构造方法的所有实现,从而name变量被设置。

注:

 2)使用修饰服,修饰方法,不需要实例化

内容包含:

import datetime
def calc_spend_time(func):
 def new_func(*args, **kargs):
  start_time = datetime.datetime.now()
  result = func(*args, **kargs)
  end_tiem = datetime.datetime.now()
  print "result:", result, "used:", (end_tiem - start_time).microseconds, "μs"
 return new_func
@calc_spend_time
def calc_add(a, b):
 return a   b
@calc_spend_time
def calc_diff(a, b):
 return a - b
calc_add(a=1, b=2)
calc_diff(1, 2)

 

C3 MRQ

我们把类 C 的线性化(MRO)记为 L[C] = [C1, C2,…,CN]。其中 C1 称为 L[C] 的头,其余元素 [C2,…,CN] 称为尾。如果一个类 C 继承自基类 B1、B2、……、BN,那么我们可以根据以下两步计算出 L[C]:
1、L[object] = [object]
2、L[C(B1…BN)] = [C] merge(L[B1]…L[BN], [B1]…[BN])
这里的关键在于 merge,其输入是一组列表,按照如下方式输出一个列表:
检查第一个列表的头元素(如 L[B1] 的头),记作 H。
若 H 未出现在其它列表的尾部,则将其输出,并将其从所有列表中删除,然后回到步骤1;否则,取出下一个列表的头部记作 H,继续该步骤。
重复上述步骤,直至列表为空或者不能再找出可以输出的元素。如果是前一种情况,则算法结束;如果是后一种情况,说明无法构建继承关系,Python 会抛出异常。

举例:

时时app平台注册网站 5

image.png

根据C3,计算过程为:

时时app平台注册网站 6

image.png

*args:把所有的参数按出现顺序打包成list
**kargs:把所有的key=value形式的参数打包成一个dict

Linux and python学习交流1,2群已满.

语法糖

 

class Human(object):
 """docstring for Human"""
 def __init__(self):
  super(Human, self).__init__()
  self.message = '111'
 def say(message):
  if not message:
   message = 'hello'
  print 'I say %s' % message
 @classmethod
 def speak(cls, message):
  if not message:
   message = 'hello'
  cls.say(message)
human = Human()
human.speak('hi')
class A():
    def func(self,x,y):
        return x * y


    @staticmethod
    def sfunc(x,y):
        return x * y


if __name__=="__main__":

    print A.sfunc(6,5)
class Human(object):
 """docstring for Human"""
 def __init__(self, value):
  super(Human, self).__init__()
  self._age = value
 @property
 def age(self):
  return self._age
human = Human(20)
print human.age

 

class Human(object):
 """docstring for Human"""
 def __init__(self):
  super(Human, self).__init__()
 @staticmethod
 def say(message):
  if not message:
   message = 'hello'
  print 'I say %s' % message
 def speak(self, message):
  self.say(message)
Human.say(None)
human = Human()
human.speak('hi')

本文实例讲述了Python装饰器(decorator)定义与用法。分享给大家供大家参考,具体如下:

更多关于Python相关内容可查看本站专题:《Python数据结构与算法教程》、《Python Socket编程技巧总结》、《Python函数使用技巧总结》、《Python字符串操作技巧汇总》及《Python入门与进阶经典教程》

import datetime
def calc_spend_time(func):
 def new_func(a, b):
  start_time = datetime.datetime.now()
  result = func(a, b)
  end_tiem = datetime.datetime.now()
  print "result:", result, "used:", (end_tiem - start_time).microseconds, "μs"
 return new_func
def calc_add(a, b):
 return a   b
calc_add = calc_spend_time(calc_add)
calc_add(1, 2)

输出:

输出同上例

Python内置的装饰器有三个:staticmethodclassmethodproperty

import datetime
def calc_spend_time(author):
 def first_deco(func):
  def new_func(*args, **kargs):
   start_time = datetime.datetime.now()
   result = func(*args, **kargs)
   end_tiem = datetime.datetime.now()
   print author, "result:", result, "used:", (end_tiem - start_time).microseconds, "μs"
  return new_func
 return first_deco
@calc_spend_time('author_1')
def calc_add(a, b):
 return a   b
@calc_spend_time('author_2')
def calc_diff(a, b):
 return a - b
calc_add(a=1, b=2)
calc_diff(1, 2)

最简单的函数,返回两个数的和

I say hello
I say hi

我们知道,在Python中函数也是被视为对象的,可以作为参数传递,那么假如把计算耗时的独立为一个单独的函数calc_spend_time(),然后把需要计算耗时的函数例如calc_add的引用传递给它,在calc_spend_time中调用calc_add,这样所有的需要计算耗时的函数都不用修改自己的代码了.

什么是装饰器(decorator)

带参数的函数装饰器

装饰器

staticmethod:把类中的方法定义为静态方法,使用staticmethod装饰的方法可以使用类或者类的实例对象来调用,不需要传入self

假如我们需要知道函数的一些额外信息,例如函数作者,可以通过给装饰器函数增加参数来实现.

Python内置装饰器

本文由时时app平台注册网站发布于编程知识,转载请注明出处:Python基础(七) python自带的三个装饰器时时app平台

关键词: