zoukankan      html  css  js  c++  java
  • python基础-面向对象(装饰器)



     属性:
      @property
      @method_name.setter
      @method_name.deleter
      三个标签都是放在方法的上面来使用,且方法名要和后续使用的
      变量名字相一致。
      
      好处:
      1 防止别人乱改变量内容
      2 在操作变量的时候,做一些附加操作,比如:写日志、写数据库
        做参数的相关计算。

    4 私有变量和私有方法:
      变量和方法前面有2个下划线。
      私有的仅限类内部使用,不能被类外部调用(不太严格)。
      私有变量和私有方法可以被继承么?不行

    5 继承:
      父类、子类、子类继承父类
      构造方法:
      1 子类没有构造方法,调用父类的构造方法,如果父类的构造方法有
      参数的话,那么实例化的时候也需要传递对应个数的参数。
      2 如果子类有构造方法,父类的构造方法此时不会被调用。
        父类构造方法有参数,那么你调用未初始化的父类实例变量会出错
        推荐子类有构造方法的时候,显式调用父类的构造方法

      多重继承:
      简单可以继承多个父类的方法和变量
      1 子类没有构造方法:调用第一个父类的构造方法,那么实例化的时候也需要传递对应个数的参数。
      2 子类有构造方法:建议:所有的父类都实例化

      方法重写:
      在子类中重新定义父类中的同名方法。
      简单来说:改造父类中不适合子类的部分。

     

    1.   新式类和经典类啥区别:
        新式类继承的时候需要使用(object)
        @method_name.setter
        @method_name.deletter
        支持新式类,不支持经典类
        @property 支持新式类和经典类 
    2. 封装:
    3. 把变量值通过实例化的方式传入到实例中来使用。
    4. 通过类中的方法(3种:类方法、实例方法、静态方法)把操作数据的逻辑进行了封装。

    with open("e:\a.txt".r) as fp:
        fp.readline()
        fp.read()
        fp.seek()

    推荐大家使用类似的封装方法。

    1. 多态

    通过调用一个方法,呈现出不同的结果。某个对象中存在重名函数,就能实现多态。

    class F1:
        pass

    class S1(F1):

        def show(self):
            print 'S1.show'

    class S2(F1):

        def show(self):
            print 'S2.show'

    def Func(obj):
        """Func函数需要接收一个F1类型或者F1子类的类型"""
        
        obj.show()
        
    s1_obj = S1()
    Func(s1_obj) # 在Func函数中传入S1类的对象 #s1_obj,执行 S1 的show方法,结果:#S1.show

    s2_obj = S2()
    Func(s2_obj) # 在Func函数中传入Ss类的对象 #ss_obj,执行 Ss 的show方法,结果:#S2.show

     

    跟基类没有关系,java存在类型,必须是基类和子类的继承关系才会存在多态;python不存在类型,所以跟基类也没有关系,只要传递的对象中有该方法就可以。

    class F1:

        pass

     

    class S1(F1):

     

        def show(self):

            print 'S1.show'

     

    class S2(F1):

     

        def show(self):

            print 'S2.show'

     

    class X:

        def show(self):

            print 'x.show'  #python中不必须是继承关系,只要有这个重名的函数就可以

     

    def Func(obj):

        """Func函数需要接收一个F1类型或者F1子类的类型"""

        

        obj.show()

        

    s1_obj = S1()

    Func(s1_obj) # 在Func函数中传入S1类的对象 #s1_obj,执行 S1 的show方法,结果:

    #S1.show

     

    s2_obj = S2()

    Func(s2_obj) # 在Func函数中传入Ss类的对象 #ss_obj,执行 Ss 的show方法,结果:

    #S2.show

     

    x_obj=X()

    Func(x_obj)

     

    1. 多态意味着可以对不同的对象使用同样的操作,但它们可能会以多种形态呈现出结果。在Python中,任何不知道对象到底是什么类型,但又需要对象做点什么的时候,都会用到多态

    #coding=utf-8

    class calculator:

        def count(self,args):

            return 1

     

    calc=calculator() 

    #自定义类型

     

    from random import choice

    #obj是随机返回的类型不确定

    obj=choice(['hello,world',[1,2,3],calc]) 

    print obj

    print type(obj)

    print obj.count('a') #方法多态

    #obj取到calc时等价于执行calc.count('a')

    #如果没有取到calc,会自动执行字符串和列表自有的count()方法,由于

    ['hello,world',[1,2,3]里面都没有a,所以打印出来了0

     

    1. 多态—” 鸭子类型

    #coding=utf-8

     

    class Duck(object):

        def quack(self): 

            print "Quaaaaaack!"

        def feathers(self): 

            print "The duck has white and gray feathers."

     

    class Person(object):

        def quack(self):

            print "The person imitates a duck."

        def feathers(self): 

            print "The person takes a feather from the ground and shows it."

     

    def in_the_forest(duck):   #必须有这么一个方法,能有接收不同类型的参数,这个方法里面的参数都是一样的。

        duck.quack()

        duck.feathers()

     

     

    donald = Duck()

    john = Person()

    in_the_forest(donald)

    in_the_forest(john)

     

    多态的好处:去除冗余代码

    有一个方法可以接受不同类型的参数,产生不同的效果。

     

    1. 多态运算符多态

        #coding=utf-8

    def add(x,y):

        return x+y

     

    print add(1,2)  #输出3

    print add("hello,","world")  #输出hello,world

    print add(1,"abc") #将抛出异常 TypeError,不同类型不能相加

     

     

    1. 方法重载

    如果你学过C++、 JAVA或者是其他面向对象编程语言,你会发现Python中的重载与其他面向对象编程语言在使用上是有区别,其实可以这么说Python在形式上是不支持重载。

     

    有相同的方法名时也能调用到最后一个,因此它是不支持方法重载的。

    在java和c中,方法是可以重载的,方法名相同参数个数不同。

     

    1. 方法重写

    重写是指子类重写父类的成员方法。子类可以改变父类方法所实现的功能,但子类中重写的方法必须与父类中对应的方法具有相同的方法名。也就是说要实现重写,就必须存在继承。
    简单来讲,就是如果父类的方法不能满足子类的需求,子类就可以重写从父类那继承过来的方法。

    方法重写必须方法名相同、参数个数相同

    #coding=utf-8

    class Parent(object):  # 定义父类
      def myMethod(self):
        print 'call Parent'

      def printName(self):
        print "my name is LiLy"

    class Child(Parent): # 定义子类
      def myMethod(self): # 子类重写父类myMethod方法
        print 'call Child'
        
        Parent.myMethod(self)
    c = Child()  # 子类实例
    c.myMethod() # 子类调用重写方法
    c.printName()

     

     

    1. 运算符重写

    #coding=utf-8

    class Vector(object) :

      def __init__(self, a, b) :

        self.a = a

        self.b = b

        print self.a,self.b

      def __str__(self): 

        print self.a,self.b

        return '----Vector (%d, %d)' % (self.a, self.b)

     

      def __add__(self,other) :

        #return Vector(self.a + other.a, self.b + other.b) #生成新的实例,print打印

    时会固定的调用str这个方法

        return self.a + other.a, self.b + other.b  #直接这样不做实例化就不会调用str方法了

     

    x =  Vector(3,7)

    y =  Vector(1, -10)

    print x + y

    #print str(x)

    #实现两个对象相加:3+1,7+-10

     

     

     

    1. __call__
      对象后面加括号,触发执行

    class Foo:

     

        def __init__(self):

            pass

       

        def __call__(self, *args, **kwargs):

            print '__call__'

     

    obj = Foo() # 执行 __init__

    obj()       # 执行 __call__

     

     

    1. 单例

    实例只有一个,此为单例;占用的都是同一块内存。因为__new__方法的存在而实现的

    #方法1,实现__new__方法
    #并在将一个类的实例绑定到类变量_instance上,
    #如果cls._instance为None说明该类还没有实例化过,实例化该类,并返回
    #如果cls._instance不为None,直接返回

     

    class Singleton(object):

        def __new__(cls, *args, **kw):  #重写是为了控制它只生成一个

            if not hasattr(cls, '_instance'):

                orig = super(Singleton, cls) #调用父类

                cls._instance = orig.__new__(cls, *args, **kw) #生成实例

            return cls._instance

     

    class MyClass(Singleton):

        a = 1

     

    one = MyClass()

    two = MyClass()

     

    two.a = 3

    print one.a

    #3

    #one和two完全相同,可以用id(), ==, is检测

    print id(one)

    #29097904

    print id(two)

    #29097904

    print one == two

    #True

    print one is two

    #True

     

     

     

    1. python对象销毁(垃圾回收)

    内存泄漏:内存一直不释放就会出现内存泄漏,功能测试是很难发现的,操作次数少

    系统连接数达不到那么多

    这些必须从稳定性测试去测

    循环引用:

     

    1. 装饰器

    在代码运行期间在不改变原函数定义的基础上,动态给该函数增加功能的方式,称之为装饰器(Decorator)。

    1)变量的工作范围

    #coding=utf-8

     

    outerVar = "this is a global variable"

    def test() :

        innerVar = "this is a Local variable"

        #outerVar+=1  #发生赋值时就会报错,因为这个变量没有定义为全局,作用域有限

        print outerVar #不赋值是不会报错的

        print n

     

    n = 10

    test()

     

     

    变量生存空间

    #coding=utf-8

     

    outerVar = "this is a global variable"

    def test() :

      innerVar = "this is a Local variable"

     

    test()

    print innerVar

     

     

     

    嵌套函数

    #coding=utf-8

     

    def outer() :

        name = "python"

        def inner() :

          print name

        return inner()

     

    outer()

    print outer()

     

    函数作为变量

    #coding=utf-8

     

    def add(x, y):

        return x + y

    def sub(x, y):

        return x - y

    def apply(func, x, y): 

        return func(x, y) 

     

    print apply(add, 2, 1) 

    print apply(sub, 2, 1)

     

     

     

    包:内置函数带有函数外的参数,在外部执行

    函数对象:就是指向函数指向的内存中的那部分地址

    #coding=utf-8

    def outer() :
      name = 1
      def inner() :
        print name   红色字体部分组成一个闭包
      return inner  #函数对象

    res = outer()
    print type(res)
    res()    #闭包:内置函数带有函数外的参数,在外部执行
    print res.func_closure #打印一下闭包里面的变量

     

     

     

     

     

    装饰器:装饰器其实就是一个闭包,把一个函数当做参数后返回一个替代版函数。

    如果我们想给now()函数增加一些别的功能,比如在调用该函数前后自动打印一些日志,但又不希望修改原now()的定义,这时候我们的装饰器就配上用场了。

    #coding=utf-8

    import time

     

    #定义装饰器

    def log(func):

      def wrapper(*args, **kw):

        print 'call func is %s' %func.__name__

    return func(*args, **kw)   #黄色字体部分形成一个闭包 

    #*args, **kw 可以接受任参数作为函数对象

      return wrapper

     

    @log 

    def now():    # @log+def now()  等价于log(now)

        now = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())

        print "current time is %s" %now

     

    now()   #加括号是为了调用函数,等价于执行wrapper

    print now  #打印出now是个函数

    log(now)  #等价于wrapper

     

     

    执行顺序:

    now --->log(now)---->返回一个闭包:带有一个变量是函数对象now,还有wrapper这个嵌套函数----》wrapper()---》显示结果:打印了一下调用函数的名字和now函数的执行结果

     

    使用@将装饰器应用到函数

    理解过程:

    #coding=utf-8

    import time

     

    #定义装饰器

    def log(func):

      def wrapper(*args, **kw):

        print 'call func is %s' %func.__name__

        return func(*args, **kw)

      return wrapper

     

     

    def now():  

        now = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())

        print "current time is %s" %now

     

     

    #now是一个函数对象,传递到log中

    #log函数的func局部变量值变成了now这个函数对象

    #执行log函数,返回的是一个闭包-->闭包是啥?是wrapper这个嵌套函数+外部变量func

    组成的,func现在是啥?func现在等于now函数

     

    #print log(now) 

    #打印结果:<function wrapper at 0x00000000026DDB38>

    #此结果返回了一个wrapper函数对象;证明log(now)返回的是一个函数

    #如何调用函数? log(now)() 加括号就可以调用

     

    log(now)()

    #call func is now

    #current time is 2017-09-24 17:26:11

    #从结果可以看到,已经成功调用了该函数

     

    #那么log(now)的另外一种写法是什么呢? 装饰器@log +def now()

    import time

     

    #定义装饰器

    def log(func):

      def wrapper(*args, **kw):

        print 'call func is %s' %func.__name__

        return func(*args, **kw)

      return wrapper

     

    @log

    def now():  

        now = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())

        print "current time is %s" %now

     

    #那么log(now)的另外一种写法是什么呢? 装饰器@log +def now(),把代码改成了这种形

    #式了,那么调用就可以简化了呗

    now()

     

    #call func is now

    #current time is 2017-09-24 17:29:17

    #call func is now

    #current time is 2017-09-24 17:29:17

    #可以看到两个的执行结果是完全一样的。

     

    小练习:写一个装饰器,把这个函数的执行时间计算出来。

    #coding=utf-8

    import time

     

    #定义装饰器

     

     

    def time_cal(func):

        def time_cal2(*args, **kw):

            start_time = time.time()

            func(*args, **kw)

            end_time = time.time()

            print end_time-start_time

            #return func(*args, **kw) #返回一下这个函数的执行结果

        return time_cal2 #返回结果是一个函数对象,是这歌函数的执行结果

            

        

     

    @time_cal

    def now():  

        now = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())

        print "current time is %s" %now

        time.sleep(5)  #直接打印时间过快,加个停顿可以看出来时间,不然会打印出来0.0

     

    now()

     

     

     

     

    第三步:

    #coding=utf-8

    def deco(func):

        print "before myfunc() called." 

        func()

        print "  after myfunc() called."

        return func

     

    @deco

    def myfunc():

        print " myfunc() called."

     

     

    #myfunc()

    #myfunc()  

     

    #E:>python c.py

    #before myfunc() called.

    # myfunc() called.

    #  after myfunc() called.

    #注释掉了调用函数,发现还是打印出来了,这就是装饰器的作用 等价于deco(myfounc

     

    第四步:

    def deco(func):
        def _deco():
            print "before myfunc() called."
            func()
            print "  after myfunc() called."
            # 不需要返回func,实际上应返回原函数的返回值
        return _deco

    @deco
    def myfunc():
        print " myfunc() called."
        return 'ok'

    @deco
    def print_mesage():
        print "my message"

    print myfunc  #返回闭包:由_deco和myfunc的函数对象组成
    myfunc()  #调用了_deco()函数
    print_mesage()

     

     

    装饰器:减少重复代码的编写

     

    装饰器的四步法:

    1. 最简单的函数,准备附加额外功能。

    打印两次

    #conding = utf-8

    def myfunc():

        print "myfunc() called"

     

    myfunc()

    myfunc()

     

     

    1. 使用装饰函数在函数执行前和执行后分别附加额外功能

    装饰函数的参数是被装饰的函数对象,返回原函数对象

    装饰的实质语句: myfunc = deco(myfunc)'''

    #conding = utf-8

    def deco(func):

        print "before myfunc() called"

        func()

        print "after myfunc() called"

        #print func

        return func

     

    def myfunc():

        print "myfunc() called"

     

    myfunc = deco(myfunc)   

    #以这样的形式去调用时,不加入return func会报错'NoneType' object is not callable

    myfunc()

    myfunc()

     

     

    1. 使用语法糖@来装饰函数,相当于“ myfunc = deco(myfunc)”但发现新函数只在第一次被调用,且原函数多调用了一次。等价于第二步程序

    #conding = utf-8

    def deco(func):

        print "before myfunc() called"

        func()

        print "after myfunc() called"

        #print func

        return func

     

    @deco

    def myfunc():

        print "myfunc() called"

     

    #myfunc = deco(myfunc)    #@@deco这;两种方式是等价的

     

    myfunc()

    myfunc()

     

    调用了两次,打出来三次,而且后两次的结果都不符合预期。

     

    1. 使用内嵌包装函数来确保每次新函数都被调用

    #conding = utf-8

    def deco(func):

        def _deco():  #使用内嵌包装函数来确保每次新函数都被调用

            print "before myfunc() called"

            func()

            print "after myfunc() called"

        #return func #不需要返回func,实际上应该返回的是原函数的返回值

        return _deco #内嵌包装函数的形参和返回值与原函数相同,装饰函数返回内嵌包装函数对象

     

    @deco

    def myfunc():

        print "myfunc() called"

     

    myfunc()

    myfunc()

     

     

     

    关于装饰器自己的分析过程:

    #conding = utf-8

    def deco(func):

        print "before myfunc() called"

        func()

        print "after myfunc() called"

        #print func

        #return func

     

    @deco

    def myfunc():

        print "myfunc() called"

    #myfunc = deco(myfunc)    #@deco这;两种方式是等价的

    myfunc()

    #myfunc()

     

    #@deco

    #def myfunc():  等价于 myfunc = deco(myfunc)

     

    #这句话的意思是 利用@deco呼叫deco这个函数,并且赋值给被@的函数名即myfunc

    #执行 步骤:

    #第一轮 (deco def myfunc():    print "myfunc() called")  

    #为啥没调用也会执行? 因为这就是@deco这个在起作用

    #1.myfunc = deco(myfunc)

    #2.print "before myfunc() called"

    #3.print "myfunc() called"

    #4.print "after myfunc() called"

    #5.没有return,默认返回一个 none   

       #此时myfunc等于none了  为啥等于none?因为deco(func)作为参数被传进来的,你  

      #不做返回它会自动返回none

     

    #第二轮: 执行myfunc()

    #上一轮执行后 myfunc成了none,后续无法执行了,报错 TypeError: 'NoneType' #object is not callable

     

    借鉴之处:https://segmentfault.com/q/1010000006990592

  • 相关阅读:
    margin问题
    IE6里面子集尺寸大的会把父亲撑大
    第一个元素<flout>写了,想在他的旁边加一个元素.IE6会出现缝隙. 不要用margin撑开,要用flout
    兼容性,float
    HTML5的兼容问题以及调用js文件的方法
    表单
    表格的编写,课程表
    SmartThreadPool
    C# 多线程的等待所有线程结束的一个问题
    DataTable保存与读取 stream
  • 原文地址:https://www.cnblogs.com/qingqing-919/p/8620378.html
Copyright © 2011-2022 走看看