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

posted @ 2018-03-21 22:50  小七Libra  阅读(239)  评论(0编辑  收藏  举报