Python基础 5.类

5.1 类定义和方法

1.类的定义: 类 是一个独立存放变量(属性/方法)的空间

1  class 类名:
2  pass
3 4  实例名 = 类名()

2.运算符“.” 调用类的属性或方法

实例可以访问类的属性
类不可以访问实例的属性
实例也是一个独立存放变量的空间
实例和实例没有关联   本身没有这个属性 就会去类中找

3.私有属性

在python中有两私有属性,分别是在属性前加  
一个下换线(_) 和两个下划线(__)        
一个下滑线外部可以直接访问,二个下划线外部不可以直接访问

4.方法

就是封装在类里的一种特殊的函数

5.“实例方法” self代表实例本身

表示的是“实例”的行为给实例用的,和函数的传参一样,只是会先传一个自身的实例self,

方法总是定义在类中的,但是却叫“实例方法”,因为它表示该类所有实例所共有的行为
1 class persson(object):
2     def eat(self):
3         print('{}正在吃烦'.format(self.name))
4         return 100
5 6 nanbei=persson()
7 nanbei.name='南北'
8 a=nanbei.eat()
9 print(a)

 

5.2 初始化和析构 (dir查看属性与方法)

Python中有很多以双下划线开头且以双下划线结尾的固定方法。他们会在特定的时机被触发执行。

init 就是其中之一,它会在实例化之后自动被调用。以完成实例的初始化。

1. init 的参数传递过程:

1 class person (object):
2     def __init__(self,name):
3         self.name=name
4         print('{}正在上课'.format(self.name))
5 6 nanbei=person('南北')
7 nanbei.__init__('南北')
8 person.__init__(nanbei,'南北')

 

2. “析构”:

通常在实例被销毁的时候, 长时间不用即可析构

__del__”就是一个析构函数了,当使用del 删除对象时,会调用他本身的析构函数。

提示开发者,对象被销毁了,方便调试。进行以写必要的清理工作。

当没有一个变量指向某个对象的时候,Python会自动销毁这个对象,以便回收内存空间。

程序报错时 相当于结束 会进行空间释放

3. del 关键字

可以删除一个变量的指向。

5.3 继承和魔术方法 super

1.继承

一个类可以继承一个类,继承之后可以使用父类的方法和属性,
公用部分可以写成父类   在子类中重写不同部分  
先在子类中找 再去父类中找

1 class Rec(object):
2     def say_hello(self):
3         print('aiwozhonghua ')
4 5 class Son(Rec):
6     pass
7 8 s=Son()
9 s.say_hello()
每个类中bases 特殊属性(返回tuple)
__bases__:查看类的直接父类
__mro__:查看类的继承路径

2.多继承

当继承多个父类时,如果父类中有相同的方法,
那么子类会优先使用最先被继承的方法   (左边的)

当子类继承父类之后,如果子类不想使用父类的方法,可以通过重写来覆盖父类的方法
 1 class Base(object): 
 2     def play (self): 
 3         print('asdasd')
 4 
 5 class A (Base):
 6     def play(self):
 7         print('健身卡')
 8 class B(Base): 
 9     def play(self): 
10         print('健身ASD卡')
11 
12 class C(A,B):
13     pass
14 15 c=C()
16 c.play()
17 print(C.__mro__)        

 

当子类重写父类方法之后,子类如果想再次调用父类的方法, 可以使用这两种方法

方法一:
 1 class C(A, B):
 2 
 3     def play(self):
 4  5         A.play(self)
 6         print('这是C')
 7  8 方法二:
 9 class C(A, B):
10 
11     def play(self):
12 13         super().play()
14         print('这是C')

 

 

3.super 函数

可以调用父类的方法  (调用上一级)
在父类中也可以使用super函数
可以通过调用类的__mor__属性或者mro方法来查看类的继承关系

 

4.魔术方法 (应用并不多)

在python中,str和repr方法在处理对象的时候,分别调用的是对象的__str__和__repr__方法

print打印对象,调用str函数,如果对象没有定义__str__方法,则调用__repr__方法处理

在交互模式下,直接输出对象,显示 __repr__ 的返回值
1.str
尽可能的提供简洁且有用的信息。
让用户尽可能吸收到必要的信息。
必须要有返回值 且是字符串类型
2.repr
尽可能向开发者提供创建该对象时的必要信息。
让开发者可以直接通过复制粘贴来重建对象。
必须要有返回值 且是字符串类型
3.类的实例向函数一样被调用
正常情况下,实例是不能像函数一样被调用的,要想实例能够被调用,就需要定义 __call__  方法
 1 class Test (object):
 2     def hi (self): 
 3         print('hi ')
 4 
 5     def __call__(self, *args, **kwargs):
 6         self.hi()
 7  8     def test2():
 9         print('haha')
10 11 t=Test()
12 t()  #此时只会调用__call__方法
13 test2()

 

4、class 查看类名
格式:  实例.__class__
5、dict 查看全部属性,返回属性和属性值键值对形式
格式:实例.__dict__
6、doc 查看对象文档,即类中(用三个引号引起来的部分)
格式:类名.__dict__
7、bases 查看父类
格式:类名.__base__
8.mro 查看多继承的情况下,子类调用父类方法时,搜索顺序
格式:子类名.__mro__
 1 class Rectang(object):
 2         def __init__(self,length,width):
 3             self.length=length
 4             self.width=width
 5     
 6         def get_area(self):
 7             print(self.length*self.width)
 8     
 9         def __add__(self, other):
10             length=self.length+other.length
11             width=self.width+other.width
12             result=Rectang(length,width)
13             return result
14     
15     class Square(Rectang):
16         def __init__(self,side_length):
17             super().__init__(side_length,side_length)
18     
19         def __call__(self, *args, **kwargs):
20             return  self.get_area()
21     
22         def __str__(self):
23             return '边长为:{}'.format(self.length)
24     
25     s=Square(150)
26     print(s)
27     s()
28     
29     a=Rectang(20,10)
30     b=Rectang(50,30)
31     new_rec=a+b  # a.__add__(b)
32     print(type(new_rec))
33     new_rec.get_area()

 

 

5.4 new方法 (重写后必须return一个实例出来)

1.new方法

return super().__new__(cls)

1、__new__方法是在类创建实例的时候自动调用的。

2、实例是通过类里面的__new__方法是在 类 创建出来的。

3、先调用__new__方法创建实例,再调用 __init__方法初始化实例。
4、__new__方法,后面括号里的cls代表的是类本身

 

在上面的例子中,我们可以看到创建实例的时候,自动调用了new,方法和init方法,并且 是先调用的new再调用的init方法,打印 cls 的时候显示的这个Base类

1 class Test (object):
2     def __init__(self):
3         print('init 方法')
4 5     def __new__(cls, *args, **kwargs):
6         print('new 方法')
7         return super().__new__(cls)
8 9 t=Test()

 

2.单例模式 (只生成一个实例)

 1     class Test (object):
 2         __insance=None  #1. 定义一个私有属性等于None
 3         def __init__(self):
 4             print('init  方法')
 5     
 6         def __new__(cls, *args, **kwargs):
 7             if cls.__insance is None:
 8                 cls.__insance=super().__new__(cls)
 9             return cls.__insance
10     a=Test
11     b=Test
12     print(a is b)
13     print(id(a))
14     print(id(b))

#2. (然后我们判断它是不是等于None,如果等于None,我们调用父类的方法创建一个实例对象,并把返回的对象赋值给 __instance,并且返回__instance)

   #3. 如果__instance不等于None,那就说明已经创建了对象 我们直接把__instance返回出去。


#单例模式实现的原理:通过重写__new__方法,让__new__只能进行一次实例创建。

#在上面的例子中,我们可以看到两个实例的ID是相同的,意味着第二次创建的时候,并没有真正的去创建,而是引用的第一次创的实例,只是同一个实例的不同名字

 

5.5 定制属性访问

  • 查:

    hasattr(re, 'length') # 返回bool值 是否有这个属性 getattr(re, 'length') # 返回属性值 获取属性的值 re . getattribute('length') 该方法在访问实例属性时,自动调用

    re . getattr('length') 该方法在访问不存在的属性时,调用

  • 改:

    setattr(re , 'length', 6) 有则改 无则增 re .setattr('length', 5) 该方法在访问实例属性时,自动调用

  • 增:

    re .aaa = 1 setattr(re, 'bbb', 2) # 有bbb属性就改,没有就增 re.setattr('ccc', 3) # 同上

  • 删:

    delattr(re , 'ccc') 删除实例属性 re .delattr('bbb') del re

 

5.6 描述方法 (了解)

__get__()       #在访问该类创建的实例时调用
__set__()       #在修改该类创建的实例时调用
__delete__()   #在删除该类创建的实例时调用

5.7 装饰器

 1     def girl():
 2         return '这是一个女孩'
 3     print(girl())
 4     
 5     def modify(func):
 6         def wrapper():
 7             result =func()
 8             return result+'很6'
 9         return wrapper
10     
11     girl= modify(girl)
12     print(girl())
13 14 15 ############################
16 17     def modify(func):
18         def wrapper():
19             result =func()
20             return result+'很6'
21         return wrapper
22     
23     @modify
24     def girl():
25         return '这是一个女孩'
26     print(girl())
27 28 29 python自带的三个内置装饰器
30 class Rectangle:
31    def __init__(self, length, width):
32         self.length = length
33         self.width = width
34    def area(self):
35         areas = self.length * self.width
36         return areas
37    @property    # 就像访问属性一样     少一个括号
38    def area(self):
39         return self.width * self.length 
40    @staticmethod    # 静态方法  和class类断开联系
41    def func():      
42         print(‘staticmethod func’)
43    @classmethod     # 类方法   
44    def show(cls):   # cls代表类本身    不用实例化即可调用 
45        print(cls)    
46        print('show fun')

 

 

5.8 类装饰器

1 class Test_Class:
2     def __init__(self, func):
3         self.func = func
4     def __call__(self):
5         print('')
6         return self.func
7 @Test_Class
8 def fun_test():
9     print('这是个测试函数')

 

类也可以做装饰器,但是需要定义 call 方法


 1 class Rectangle:
 2         def __init__(self, length, width):
 3             self.length = length
 4             self.width = width
 5     
 6         def area(self):
 7             areas = self.length * self.width
 8             return areas
 9         @property
10         def get_area(self):
11             return self.length*self.width
12         @staticmethod   # 静态方法  和class类断开联系
13         def func():
14             print('staticmethod func')
15     
16     rec=Rectangle(10,8)
17     print(rec.get_area)
18     Rectangle.func()
19 20 ########################################    
21     # 类装饰器
22     
23     import time
24     class Myclass (object):
25         def __init__(self,func):
26             self.func=func
27     
28         def __call__(self, *args, **kwargs):
29             start_time = time.time()
30             self.func(*args)
31             r_time = time.time() - start_time
32             print(r_time)
33     
34     @Myclass
35     def girl (name):
36         print('girl name is {} '.format(name ))
37     
38     girl('ada')
39 40 ########################################
41 42 查看函数运行时间:
43 44     import time
45     def run_time (func):
46         def wrapper():
47             start_time=time.time()
48             result=func()
49             r_time=time.time()-start_time
50             print(r_time)
51             return  result
52         return wrapper
53     
54     @run_time
55     def say_hello():
56         print('hello')
57     
58     say_hello()
59 60 61 ########################################
62 63     import time
64     def run_time (func):
65         def wrapper():
66             start_time=time.time()
67             func()
68             t=time.time()-start_time
69             print('shijianwei{} '.format(t))
70         return wrapper()
71     
72     @run_time
73     def test_type():
74         for i in range(1000):
75             type(1)
76     
77     @run_time
78     def test_isinstance():
79         for i in range(1000):
80             isinstance(1,int)

 

5.9、补充

5.9.1、什么是装饰器?

装饰器本质上就是一个python函数,他可以让其他函数在不需要做任何代码变动的前提下,

增加额外的功能,装饰器的返回值也是一个函数对象。

5.9.2、装饰器的原则

  •  不修改被装饰对象的源代码
  • 不修改被装饰对象的调用方式

5.9.3、装饰器的目标

在遵守装饰器原则的前提下,为被装饰对象添加上新功能

5.9.4、装饰器应用场景:插入日志,性能测试,事务处理,缓存等等

5.9.5、装饰器的形成过程

 现在有一个需求,需要在不改变原函数代码的情况下去计算这个函数的执行时间

 1 import time
 2 def fun():
 3     time.sleep(1)
 4     print("hello world")
 5 def timer(f):
 6     def inner():
 7         start = time.time()
 8         f()
 9         end = time.time()
10         print("run time is %s"%(end-start))
11     return inner
12 fun = timer(fun)
13 fun()

 

执行结果:

如果有多个函数想让你测试他们的执行时间,每次是不是都得是:函数名=timer(函数名),这样还是有点麻烦,所以更简单的方法就是Python提供的语法糖

 1 import time
 2 def timer(f):
 3     def inner():
 4         start = time.time()
 5         f()
 6         end = time.time()
 7         print("run time is %s"%(end-start))
 8     return inner
 9 @timer  #语法糖
10 def fun():
11     time.sleep(1)
12     print("hello world")
13  
14 fun()

 

 刚刚我们讨论的装饰器都是装饰不带参数的函数,现在要装饰一个带参数的函数怎么办呢?

 1 import time
 2 def timer(f):
 3     def inner(*args,**kwargs):
 4         start = time.time()
 5         f(*args,**kwargs)
 6         end = time.time()
 7         print("run time is %s"%(end-start))
 8     return inner
 9 @timer  #语法糖
10 def fun(a):
11     print(a)
12     time.sleep(1)
13     print("hello world")
14 @timer
15 def fun1(a,b):
16     print(a,b)
17  
18 fun(1)
19 fun1(23,21)

 

 执行结果:

 

5.9.6、带参数的装饰器

假如你有500个函数使用了一个装饰器,现在你需要把这些装饰器都取消掉,你要怎么做?过两天你又需要加上...

 1 import time
 2 flag=False  #通过flag的值来判断是否进行装饰
 3 def fun(flag):
 4     def demo(func):
 5         def inner(*args,**kwargs):
 6             if flag:
 7                 start=time.time()
 8                 func(*args,**kwargs)
 9                 end=time.time()
10                 print("run time is %s"%(end-start))
11             else:
12                 func(*args,**kwargs)
13         return inner
14     return demo
15 @fun(flag)
16 def fun1():
17     time.sleep(0.1)
18     print("welocme you")
19  
20 fun1()

 

登录认证例子:

 1 user = "crazyjump"
 2 passwd = "123"
 3 def auth(auth_type):
 4     def func(f):
 5         def wrapper(*args, **kwargs):
 6             if auth_type == "local":
 7                 username = input("Username:").strip()
 8                 password = input("Password:").strip()
 9                 if user == username and passwd == password:
10                     print("\033[32;1mlogin successful\033[0m")
11                     f()
12                 else:
13                     exit("\033[31;1mInvalid username or password\033[0m")
14             elif auth_type == "ldap":
15                 f()
16                 print("不会")
17         return wrapper
18     return func
19 @auth(auth_type="local")
20 def crazyjump():
21     print("welcome to crazyjump")
22 crazyjump()

 

 5.9.7、多个装饰器装饰一个函数

fun1和fun2的加载顺序:自下而上

inner函数的执行顺序为:自上而下

 1 def fun1(func):
 2     def inner():
 3         print('fun1 ,before')
 4         func()
 5         print('fun1 ,after')
 6     return inner
 7  
 8 def fun2(func):
 9     def inner():
10         print('fun2 ,before')
11         func()
12         print('fun2 ,after')
13     return inner
14  
15 @fun2
16 @fun1
17 def f():
18     print('in the f')
19 f()

 

执行结果:

5.9.8、开放封闭原则

  • 对扩展是开放的

    为什么要对扩展开放呢?

    我们说,任何一个程序,不可能在设计之初就已经想好了所有的功能并且未来不做任何更新和修改。所以我们必须允许代码扩展、添加新功能。

  • 对修改是封闭的

    为什么要对修改封闭呢?

    就像我们刚刚提到的,因为我们写的一个函数,很有可能已经交付给其他人使用了,如果这个时候我们对其进行了修改,很有可能影响其他已经在使用该函数的用户。

装饰器完美的遵循了这个开放封闭原则。

 



 

posted on 2019-10-15 00:51  杜育非  阅读(217)  评论(0编辑  收藏  举报

导航