Python第12天笔记

装饰器

装饰器存在的意义:

  • 不影响原有函数的功能
  • 可以添加新功能

一般常见的比如拿到第三方API接口,第三方不允许修改这个接口。这个时候装饰器就派上用场了。

装饰器本身也是一个函数,作用是为现有存在的函数,在不改变函数的基础上,增加一些功能进行装饰。是以闭包的形式实现的,在使用装饰器函数时,在被装饰的函数前一行,使用@装饰器函数名形式来进行装饰。

#demo:

在一个项目中,有很多函数由于项目越来越大,功能越来越多,导致程序越来越慢。其中一个功能函数的功能,是实现一百万次的累加。

import time
def my_count():
    s=0
    for i in range(1000001):
        s+=i
    print("sum:",s)
start=time.time()
my_count()
end=time.time()
print("执行时间为:",(end-start))

#输出结果:
sum: 500000500000
执行时间为: 0.13900399208068848

实现了时间计算,但如果有成千上万的函数,就会显得冗余。

def my_count():
    s=0
    for i in range(1000001):
        s+=i
    print("sum:",s)
def count_time(func):
    start=time.time()
    func()
    end=time.time()
    print("执行时间:",(end-start))
count_time(my_count)

经过修改后,定义一个函数来实现时间计算功能。但是在使用时需要将对应的函数传入到时间计算函数中。但仍然影响了原来的使用,那么能不能在使用时不影响函数原来的使用方式,同时计算时间:

def count_time(func):
    def wrapper():
        start=time.time()
        func()
        end=time.time()
        print("执行时间为:",(end-start))
    return wrapper

@count_time
def my_count():
    s=0
    for i in range(1000001):
        s+=i
    print("sum:",s)
# my_count=count_time(my_count)
my_count()

这样实现的好处,定义闭包函数后,只需要通过@装饰器函数名形式的装饰器语法,就可以将@装饰器函数名加到要装饰的函数前即可。

这种不改变原有函数功能,对函数进行拓展的形式,就称为装饰器

在执行@装饰器函数名时,就是将原函数传递到闭包中。然后原函数的引用指向闭包返回的装饰过的内部函数的引用。

#装饰器的几种形式:

  • 无参无返回值
def setFunc(func):
    def wrapper():
        print("Start")
        func()
        print("end")
    return wrapper

@setFunc
def show():
    return "show"
print(show())

#输出结果:
Start
end
None
  • 无参有返回值
def setFunc(func):
    def wrapper():
        print("Start")
        return func()
        print("end") #遇到return此句不再执行。
    return wrapper

@setFunc
def show():
    return "show"
print(show())

#输出结果:
Start
show
  • 有参无返回值
def setFunc(func):
    def wrapper(x,y):
        print("Start")
        func(x,y)
        print("end")
    return wrapper

@setFunc
def myAdd(x,y):
    return x+y
print(myAdd(3,3))

#输出结果:
Start
end
None
  • 有参有返回值
def setFunc(func):
    def wrapper(x,y):
        print("Start")
        return func(x,y)
        print("end")
    return wrapper

@setFunc
def myAdd(x,y):
    return x+y
print(myAdd(3,3))

#输出结果:
Start
6

根据被装饰函数的定义不同,分出了四种类型。

能否实现适用于任何形式函数定义:

def setFunc(func):
    def wrapper(*args,**kwargs):
        print("Wrapper context.")
        return func(*args,**kwargs)
    return wrapper
@setFunc
def func(name,age,job="IT"):
    print(name,age,job)
func("Tom",18,"student")

#输出结果:
Wrapper context.
Tom 18 student
def demo(a,b,*c,**d):
    print((a,b))
    print(c)
    print(d)
demo("city","cllege",1999,1,1,school="zhengjiang University")

#输出结果:
('city', 'cllege')
(1999, 1, 1)
{'school': 'zhengjiang University'}

#函数被多个装饰器装饰:

一个函数在使用时,通过一个装饰器来拓展,可能不能达到预期。

def setFunc1(func):
    def wrapper1(*args,**kwargs):
        print("Wrapper Context 1 Start.".center(40,'-'))
        func(*args,**kwargs)
        print("Wrapper Context 1 End.".center(40,'-'))
    return wrapper1
def setFunc2(func):
    def wrapper2(*args,**kwargs):
        print("Wrapper Context 2 Start.".center(40,'-'))
        func(*args,**kwargs)
        print("Wrapper Context 2 End.".center(40,'-'))
    return wrapper2
@setFunc1
@setFunc2
def show(*args,**kwargs):
    print("Show Run".center(40,'-'))
show()

#输出结果:
--------Wrapper Context 1 Start.--------
--------Wrapper Context 2 Start.--------
----------------Show Run----------------
---------Wrapper Context 2 End.---------
---------Wrapper Context 1 End.---------

##从下往上去装饰(程序语句运行顺序来看)

##从内往外去装饰(函数的调用来看)

总结:

1、函数可以像普通变量一样,作为函数的参数或者返回值进行传递;

2、函数的内部可以定义另外一个函数。目的隐藏函数功能的实现;

3、闭包实际上也是函数定义的一种形式

4、闭包定义的规则,在外部函数内定义一个内部函数,内部函数使用外部函数的变量,并返回内部函数的引用

5、Python中,装饰器就是用闭包实现的

6、装饰器的作用,不改变现有函数的基础上为函数增加功能

7、装饰器的使用,通过@装饰器函数名的形式来给已有函数进行装饰,添加功能

8、装饰器的四种形式,根据参数的不同以及返回值的不同

9、万能装饰器,通过可变参数(*args/**kwargs)来实现

10、一个装饰器可以为多个函数提供装饰功能

11、一个函数也可以被多个装饰器所装饰

12、通过类实现装饰器,重写__init____call__函数

13、类装饰器在装饰函数后,原来的引用不再是函数,而是装饰类的对象。

静态方法和类方法

1、静态方法:

  • 通过装饰器@staticmethod来进行装饰。静态方法既不需要传递类对象也不需要传递实例对象;
  • 静态方法也可以通过实例对象和类对象去访问
class Dog:
    type="狗"
    def __init__(self):
        name=None
    # 静态方法
    @staticmethod
    def introduce():
        print("犬科哺乳动物,属于肉食目。")
dog=Dog()
Dog.introduce()
dog.introduce()

静态方法是类中的函数,不需要实例。静态方法主要是用来存放逻辑性的代码,逻辑上属于类,但和类本身没有关系,也就是说在静态方法中,不会涉及类中属性和方法的操作。

静态方法是一个独立的单纯的函数,仅仅是托管于某个类的名称空间中,便于维护和管理。

##使用场景:

  • 当方法中 既不需要使用实例对象(如实例对象,实例属性),也不需要使用类对象 (如类属性、类方法、创建实例等)时,定义静态方法
  • 取消不需要的参数传递,有利于 减少不必要的内存占用和性能消耗
  • 如果在类外面写一个同样的函数来做这些事,打乱了逻辑关系,导致代码维护困难,使用静态方法

类方法

  • 类对象所拥有的方法
  • 需要用装饰器@classmethod来标识其为类方法。
  • 对于类方法,第一个参数必须是类对象,一般以cls作为第一个参数
class Dog:
    __type="狗"
    @classmethod
    def get_type(cls):
        return cls.__type
print(Dog.get_type())

使用场景:

  • 当方法中需要使用类对象(如访问私有类属性等)时,定义类方法;
  • 类方法一般和类属性配合使用。

注:

类中定义了同名对象方法,类方法以及静态方法时,调用方法会优先执行最后定义的方法。

class Dog:
    def demo_method(self):
        print("对象方法")
    @classmethod
    def demo_method(cls):
        print("类方法")
    @staticmethod
    def demo_method():  #最后被定义,调用时优先执行
        print("静态方法")
dog=Dog()
Dog.demo_method()

#property:

##概述:

在Python中主要为属性提供一个便利的操作方式。

如果我们现在需要设计一个银行账户类,这个类中包含账户人姓名、余额,

简单实现:

class Account(object):
    def __init__(self,name,money):
        self.name=name
        self.money=money

问题:

不安全(设计很简单方便,所有的属性,外部可以访问修改非常不安全)

改进1 隐藏实现细节

对于账户信息而言,金额不允许让用户直接修改。如果修改只能去窗口办理。

在使用对象时,尽量不要让使用者直接操作对象中的属性,因为直接操作会带来安全隐患。

class Account(object):
    def __init__(self,name,money):
        self.__name=name
        self.__money=money

代码改进后,将所有属性设计成私有属性后。确实从外部使用时,不知道内部的属性,不能直接修改对象,隐藏了实现细节。

改进2 提供一个精确的访问

class Account(object):
    def __init__(self,name,money):
        self.__name=name
        self.__balance=money
    def get_name(self):
        return self.__name
    def set_balance(self,money):
        self.__balance=money
    def get_balance(self):
        return self.__balance

经过修改,外部使用这个类的对象时,想使用对象中的属性,只能通过类中提供的 set/get 接口来操作,提高了程序的安全性。

这样,程序基本达到了设计需求,但是能不能更加完善呢?

如果在使用这个类的对象过程中,由于误操作,传入了不正常的数据,导致数据异常。该如何以避免这种情况发生呢?

比如:设置金额时出现了负数,或字符串,或其它类型的对象。

改进3 保证数据的有效性

set_balance方法中,

class Account(object):
    def __init__(self,name,money):
        self.__name=name
        self.__balance=money

    def get_name(self):
        return self.__name

    def set_balance(self,money):
        if isinstance(money,int):
            if money>0:
                self.__balance = money
            else:
                raise ValueError('输入金额不正确')
        else:
            raise ValueError('输入金额不是数字')

    def get_balance(self):
        return self.__balance

经过几个版本的迭代,程序看上去强壮了,安全性也越来越高。

Property类

在Python中,提供了一个property类,通过对创建这个类的对象的设置,在使用对象的私有属性时,可以不再使用属性的函数调用方式,而是相对普通的公有属性一样去使用属性,为开发者提供便利。

#格式:
property(fget=None,fset=None,fdel=None,doc=None)

property是一个类,__init__方法由四个参数组成,实例后返回一个用来操作属性的对象

  • fget:属性的获取方法
  • fset:属性的设置方法
  • fdel:属性的删除方法
  • doc:属性描述
class Account(object):
    def __init__(self,name,money):
        self.__name=name
        self.__balance=money

    def __get_name(self):
        return self.__name

    def set_balance(self,money):
        if isinstance(money,int):
            if money>0:
                self.__balance = money
            else:
                raise ValueError('输入金额不正确')
        else:
            raise ValueError('输入金额不是数字')

    def get_balance(self):
        return self.__balance
    name=property(__get_name)
    balance=property(get_balance,set_balance)
ac=Account("Tom",10000)
print(ac.name)
print(ac.balance)
ac.balance=1000
print(ac.balance)

#输出结果:
Tom
10000
1000
class Account(object):
    def __init__(self,name,money):
        self.__name=name
        self.__balance=money

    @property
    def name(self):
        return self.__name

    @property
    def balance(self):
        return self.__balance

    @balance.setter
    def balance(self,money):
        if isinstance(money,int):
            if money>0:
                self.__balance = money
            else:
                raise ValueError('输入金额不正确')
        else:
            raise ValueError('输入金额不是数字')

ac=Account("Tom",10000)
print(ac.name)
print(ac.balance)
ac.balance=1000
print(ac.balance)

self

如果对象的方法中需要使用该对象的属性:

  • 关键字self主要用于对象方法中,表示调用该方法的对象;
  • 在方法中使用self,可以获取到调用当前方法的对象,进而获取该对象的属性和方法。

调用对象的方法时,为什么不需要设置self对应的参数?

  • 某个对象调用其方法时,Python解释器会把这个对象
class Cat:
    def introduce(self):
        print("name is: %s,age is:%d"%(self.name,self.age))
#实例化,创建一个对象
cat=Cat()
cat.name="蓝猫"
cat.age=6
cat.introduce()
#创建另一个对象
catt=Cat()
catt.name="英短"
catt.age=3
catt.introduce()

#输出结果:
name is: 蓝猫,age is:6
name is: 英短,age is:3

方法内定义属性

  • 使用self操作属性和对象的变量名在效果上类似。如果属性在赋值时还没有被定义。就会自动定义一个属性并赋值。
class Cat:
    def introduce(self):
        self.type="小型动物"
cat=Cat()
cat.introduce()
print(cat.type)

__new__方法

  • 创建对象时,系统会自动调用__new__方法
  • 开发者可以使用__new__方法来自定义对象的创建过程

1、__new__至少要有一个参数cls,代表要实例化的类,此参数在实例化时由Python解释器自动提供

2、__new__必须要有返回值,返回实例化出来的实例,这点在自己实现__new__时要特别注意,可以return父类__new__出来的实例,或者直接是object的__new__出来的实例

3、__init__有一个参数self,就是这个__new__返回的实例,__init____new__的基础上可以完成一些其它初始化的动作,__init__不需要返回值

4、如果创建对象时传递了自定义参数,且重写了new方法,则new也必须 “预留” 该形参,否则init方法将无法获取到该参数

__call__方法

对象后面加括号,触发执行。构造方法的执行是由创建对象触发的,即对象=类名()

而对于__call__方法的执行是由对象后加括号触发的,即对象()或者类()

class A(object):
    def __init__(self):
        pass
    def __call__(self,x):
        print('__call__called,print x:',x)
a=A()
a('123')

#输出结果:
__call__called,print x: 123
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值