Python最近挺火呀,比鹿晗薛之谦还要火,当然是在程序员之间。下面我们看看有关Python的相关内容。
上一篇文章我们已经介绍了部分Python面向对象编程基础的知识,大家可以参阅:Python面向对象编程基础解析(一),接下来,我们看看另一篇。
封装
1.为什么要封装?
封装就是要把数据属性和方法的具体实现细节隐藏起来,只提供一个接口。封装可以不用关心对象是如何构建的,其实在面向对象中,封装其实是最考验水平的
2.封装包括数据的封装和函数的封装,数据的封装是为了保护隐私,函数的封装是为了隔离复杂度
3.数据的封装就是在属性前面加一个__
1
2
3
4
5
6
7
8
9
|
class People: def __init__( self ,name,age,salary): self .name = name self .age = age self .__salary = salary p = People( 'zhang' , 19 , 100000 ) print (p.name) #zhang print (p.age) #19 print (p.__salary) #AttributeError: 'People' object has no attribute '__salary' |
咦,报错了,让我们打开对象的名称空间,看看发生了什么
1
|
print (p.__dict__) #{'name': 'zhang', 'age': 19, '_People__salary': 100000} |
哦,原来python把__salary变形成了_People__salary,再来一遍
1
|
print (p._People__salary) #100000 |
所以,Python中并没有绝对的隐藏,只要你知道了上面这个,就无所谓隐藏了
这些变形操作,只在类的定义阶段或者对象定义(实例化阶段)阶段发生
虽然在外部无法直接访问加了__的属性,但是在类内部可以访问到,可以这么理解,在定义阶段,只要遇到__开头的,Python解释器自动识别为_类名__属性,所以在类内部是可以访问到的,这样的话,我们就可以搞一点小事情了
先来看这个
1
2
3
4
5
6
7
8
9
10
11
12
|
class A: def foo( self ): print ( 'from A foo' ) self .bar() def bar( self ): print ( 'from A bar' ) class B(A): def bar( self ): print ( 'from B bar' ) b = B() b.foo() #from A foo #from B bar 别想多了,调用函数时别看定义位置,要看调用位置 |
如果就是想调用父类的bar()函数呢?该怎么做
1
2
3
4
5
6
7
8
9
10
11
12
|
class A: def foo( self ): print ( 'from A foo' ) self .__bar() def __bar( self ): print ( 'from A bar' ) class B(A): def __bar( self ): print ( 'from B bar' ) b = B() b.foo() #from A foo #from A bar 有没有感受到编程的美妙 |
4.封装的应用
1)不让外界看到我们的数据属性是怎么定义的,只能通过我们提供的接口,看到我们允许外界看到的内容
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
class People: def __init__( self ,name,age,height,weight,hobby): self .__name = name self .__age = age self .__height = height self .__weight = weight self ._hobby = hobby def tell_info( self ): print ( ''' name:%s age:%s height:%s weeight:%s ''' % ( self .__name, self .__age, self .__height, self .__weight)) p = People( 'zhang' , 18 , 1.90 , 75 , 'read' ) p.tell_info() |
2)更常用的场景是,我们可以限制数据的类型,添加自己的逻辑以后再封装好给用户
1
2
3
4
5
6
7
|
def tell_name( self ): print ( self .__name) #修改名字 def set_name( self ,new): if not isinstance (new, str ): raise TypeError( '名字必须是字符串类型' ) self .__name = new |
5.看我们上面的操作,用户查看名字的时候还得p.tell_name(),本来是个数据属性,却被我们搞得变成了一个函数,怎么伪装一下呢,就可以用到property这个装饰器了
1
2
3
4
5
6
7
8
9
10
11
12
|
class People: def __init__( self ,name,age,height,weight,hobby): self .__name = name self .__age = age self .__height = height self .__weight = weight self ._hobby = hobby @property def name( self ): return self .__name p = People( 'zhang' , 18 , 1.90 , 75 , 'read' ) print (p.name) #zhang |
数据属性还应该有修改,删除操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
@property def name( self ): return self .__name #name已经被property修饰过,就有setter和deleter @name .setter def name( self ,new): if not isinstance (new, str ): raise TypeError( '名字必须是字符串类型' ) self .__name = new @name .deleter def name( self ): del self .__name p = People( 'zhang' , 18 , 1.90 , 75 , 'read' ) print (p.name) #zhang p.name = 'can' #修改 print (p.name) #can del p.name #删除 print (p.name) #AttributeError: 'People' object has no attribute '_People__name' |
1.多态
多态的概念虽然现在才说,但是我们一直在用。多态就是多种形态的意思,动物都猫,狗,猪等等,这些都是动物的多种形态。
反映在Python中,多态就意味着就算不知道变量所引用的对象类型是什么,也能对它进行操作。比如序列类型有计算长度的方法len(),那我们拿到一个序列类型x,我们不需要知道x是什么类型,只需要知道它是序列类型,那我们就可以用len(x)的方法计算x的长度。这就是多态性,Python本身就是多态的
当然我们学过一个内置函数isinstance(),可以用它来判断数据类型,但是这可不符合多态优美的特性
2.绑定方法与非绑定方法
类里面定义的函数有两种,绑定方法和非绑定方法
1)绑定方法
绑定方法又分为绑定给类的方法和绑定给对象用的方法。
凡是定义在类中,且没有被任何装饰器修饰的方法,都是绑定给对象用的方法。
特点是obj.func()会自动把obj当作第一个参数传入,因为func的逻辑就是要处理obj
在类中定义的被classmethod装饰器修饰的方法,就是绑定给类的方法
特点是cls.func()会自动把类cls当作第一个参数传入,因为func的逻辑是处理cls,就算是对象调用这个函数,也是把类当成第一个参数传入
1
2
3
4
5
6
7
8
9
10
11
|
class People: def __init__( self ,name): self .name = name def bar( self ): print ( '----->' , self .name) @classmethod def func( cls ): print ( cls ) p1 = People( 'zhang' ) p1.func() #<class '__main__.People'> People.func() #<class '__main__.People'> |
2)非绑定方法
还有一种方法是既不绑定给类,也不绑定给对象的,叫做非绑定方法
用staticmethod装饰器装饰
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
#文件名:pickle_test import hashlib import time import pickle import os student_path = r 'C:\Users\Administrator\PycharmProjects\test\student' class People: def __init__( self ,name,sex,user_id): self .name = name self .sex = sex self .user_id = user_id self . id = self .create_id() def tell_info( self ): print ( ''' --------%s info-------- id:%s name:%s sex:%s user_id:%s ''' % ( self .name, self . id , self .name, self .sex, self .user_id)) def create_id( self ): m = hashlib.md5() m.update( self .name.encode( 'utf-8' )) m.update( self .sex.encode( 'utf-8' )) m.update( str ( self .user_id).encode( 'utf-8' )) return m.hexdigest() def save( self ): with open ( self . id , 'wb' ) as f: pickle.dump( self ,f) @staticmethod #非绑定方法,就是一个函数,就是一个工具而已,不需要类,也不需对象 def get_all(): res = os.listdir(student_path) for item in res: file_path = r '%s\%s' % (student_path,item) with open (file_path, 'rb' ) as f: obj = pickle.load(f) obj.tell_info() #反序列化.py from pickle_test import People p = People( 'zhang' , 'male' , 123123123 ) p.get_all() |
3.软件开发规范
在真正的软件开发过程中,并不是把所有代码写在一个文件中的,可以想象一下,一个小程序大概有10000行代码,都写在一个文件中的话,你还干这行吗?
正确的方式应该是把程序拆成一个一个模块。比如常用的bin目录放可执行文件,conf目录放配置文件
db目录放数据,log目录放日志文件,lib目录放库,src目录放一些重要的代码,比如重要的逻辑,类的定义等
总结
以上就是本文关于Python面向对象编程基础解析的全部内容,希望对大家有所帮助。如有不足之处,欢迎留言指出。感谢朋友们对本站的支持!
原文链接:http://www.cnblogs.com/zhang-can/p/7142544.html