史上最全Python面向对象编程

面向对象编程和函数式编程(面向过程编程)都是程序设计的方法,不过稍有区别。编程

面向过程编程:api

1. 导入各类外部库ide

2. 设计各类全局变量函数式编程

3. 写一个函数完成某个功能函数

4. 写一个函数完成某个功能测试

5. 写一个函数完成某个功能网站

6. 写一个函数完成某个功能ui

7. 写一个函数完成某个功能this

8. ......url

9. 写一个main函数做为程序入口

在多函数程序中,许多重要的数据被放置在全局数据区,这样它们能够被全部的函数访问。每一个函数均可以具备它们本身的局部数据,将某些功能代码封装到函数中,往后便无需重复编写,仅调用函数便可。从代码的组织形式来看就是根据业务逻辑从上到下垒代码 。

面向对象编程:

1. 导入各类外部库

2. 设计各类全局变量

3. 决定你要的类

4. 给每一个类提供完整的一组操做

5. 明确地使用继承来表现不一样类之间的共同点

6. 根据须要,决定是否写一个main函数做为程序入口

面向对象编程中,将函数和变量进一步封装成类,类才是程序的基本元素,它将数据和操做紧密地连结在一块儿,并保护数据不会被外界的函数意外地改变。类和和类的实例(也称对象)是面向对象的核心概念,是和面向过程编程、函数式编程的根本区别。

并非非要用面向对象编程,要看你的程序怎么设计方便,可是就目前来讲,基本上都是在使用面向对象编程。

类的基本用法

面向对象是经过定义class类来定义,这么说面向对象编程就是只使用class类,在class类中有封装,继承的功能,而且还能够构造要传入的参数,方便控制。

案例一

 
  1. import sys 
  2. import time 
  3. reload(sys) 
  4. sys.setdefaultencoding('utf-8') 
  5.  
  6. class studetn: 
  7.     # 定义一个类名为studetn 
  8.     def __init__(self,idx): 
  9.     # 定义初始化构造,这里使用init,还有别的属性好比reversed,iter之类的 
  10.         self.idx=idx 
  11.         # 初始化变量,方便继承 
  12.     def runx(self): 
  13.     # 定义运行函数,从上面继承变量 
  14.         print self.idx 
  15.         # 打印出idx的值,或者作一些别的处理 
  16.         time.sleep(1) 
  17. a=studetn('a') 
  18. a.runx() 
  19. # 这是类的调用,必定要记得类的使用方法,首先传入参数,类赋值给一个变量a 
  20. # 而后调用这个类下面定义的函数 

一些专业术语概念,既然有面向对象编程这个高大上的定义了,天然要搭配一些高大上的概念。

  1. 类(Class): 用来描述具备相同属性和方法的对象的集合。它定义了该集合中每一个对象所共有的属性和方法。其中的对象被称做类的实例。
  2. 实例:也称对象。经过类定义的初始化方法,赋予具体的值,成为一个”有血有肉的实体”。
  3. 实例化:建立类的实例的过程或操做。
  4. 实例变量:定义在实例中的变量,只做用于当前实例。
  5. 类变量:类变量是全部实例公有的变量。类变量定义在类中,但在方法体以外。
  6. 数据成员:类变量、实例变量、方法、类方法、静态方法和属性等的统称。
  7. 方法:类中定义的函数。
  8. 静态方法:不须要实例化就能够由类执行的方法
  9. 类方法:类方法是将类自己做为对象进行操做的方法。
  10. 方法重写:若是从父类继承的方法不能知足子类的需求,能够对父类的方法进行改写,这个过程也称override。
  11. 封装:将内部实现包裹起来,对外透明,提供api接口进行调用的机制
  12. 继承:即一个派生类(derived class)继承父类(base class)的变量和方法。
  13. 多态:根据对象类型的不一样以不一样的方式进行处理。

类与实例

 
  1. # -*- coding: utf-8 -*- 
  2. @Time    : 2018/5/3 0003 17:02 
  3. @Author  : Langzi 
  4. @Blog    : www.langzi.fun 
  5. @File    : 面向对象2.py 
  6. @Software: PyCharm 
  7. import sys 
  8. import time 
  9. import requests 
  10. reload(sys) 
  11. sys.setdefaultencoding('utf-8') 
  12.  
  13. class cc: 
  14.     ccc = 'ccc' 
  15.     # cc就是类名 若是想要继承别的类 就class cc(threading) 意思就是从threading继承 
  16.     def __init__(self,a,b,c): 
  17.         self.a=a 
  18.         self.b=b 
  19.         self.c=c 
  20.         # 定义构造的过程就是实例化 
  21.     def runx(self): 
  22.         print self.a*10 
  23.         print self.b*5 
  24.         print self.c*2 
  25.     def runy(self): 
  26.         print requests.get('http://www.langzi.fun').headers 
  27. e = cc('AAA','CCC','EEE') 
  28. e.runx() 
  29. e.runy() 
  30. # 这两个就是调用类里面的方法 
  31. print e.c 
  32. #实例变量指的是实例自己拥有的变量。每一个实例的变量在内存中都不同。 
  33. print e.ccc 
  34. #类变量,在类里面找到定义的变量。 
  35. 调用类的三种方法 

调用类的三种方法

实例方法

 
  1. # -*- coding: utf-8 -*- 
  2. # @Time    : 2018/5/3 0003 17:16 
  3. # @Author  : Langzi 
  4. # @Blog    : www.langzi.fun 
  5. # @File    : 面向对象3.py 
  6. # @Software: PyCharm 
  7. import sys 
  8. import time 
  9. import requests 
  10. reload(sys) 
  11. sys.setdefaultencoding('utf-8') 
  12.  
  13. class dd: 
  14.     def __init__(self,url): 
  15.         self.url=url 
  16.     def runx(self): 
  17.         print requests.get(self.url).status_code 
  18.  
  19. a = dd('http://www.langzi.fun') 
  20. a.runx() 
  21. # 这种调用方法就是实例方法 

静态方法

静态方法由类调用,无默认参数。将实例方法参数中的self去掉,而后在方法定义上方加上@staticmethod,就成为静态方法。它属于类,和实例无关。建议只使用类名.静态方法的调用方式。(虽然也可使用实例名.静态方法的方式调用)

 
  1. # -*- coding: utf-8 -*- 
  2. # @Time    : 2018/5/3 0003 17:21 
  3. # @Author  : Langzi 
  4. # @Blog    : www.langzi.fun 
  5. # @File    : 面向对象4.py 
  6. # @Software: PyCharm 
  7. import sys 
  8. import requests 
  9. reload(sys) 
  10. sys.setdefaultencoding('utf-8') 
  11. class ff: 
  12.     @staticmethod 
  13.     def runx(): 
  14.         print requests.get('http://www.langzi.fun').status_code 
  15. ff.runx() 
  16. #这里就直接调用了类的变量,只在类中运行而不在实例中运行的方法 

常常有一些跟类有关系的功能但在运行时又不须要实例和类参与的状况下须要用到静态方法. 好比更改环境变量或者修改其余类的属性等能用到静态方法. 这种状况能够直接用函数解决, 但这样一样会扩散类内部的代码,形成维护困难。

类方法

类方法由类调用,采用@classmethod装饰,至少传入一个cls(代指类自己,相似self)参数。执行类方法时,自动将调用该方法的类赋值给cls。建议只使用类名.类方法的调用方式。(虽然也可使用实例名.类方法的方式调用)

实际案例

若是要构造一个类,接受一个网站和这个网站的状态码,而后打印出来。就像这样:

 
  1. import sys 
  2. import requests 
  3. reload(sys) 
  4. sys.setdefaultencoding('utf-8') 
  5. class gg: 
  6.     def __init__(self,url,stat): 
  7.         self.url=url 
  8.         self.stat=stat 
  9.     def outer(self): 
  10.         print self.url 
  11.         print self.stat 
  12. a = gg('langzi',200) 
  13. a.outer() 

这样就是使用实例方法,虽然能够实现,可是有的时候传入的参数并非(‘langzi’,200)这样的格式,而是(‘langzi-200’)这样的,那该怎么作?首先要把这个拆分,可是要使用实例方法实现起来很麻烦,这个时候就可使用类方法。

 
  1. # -*- coding: utf-8 -*- 
  2. # @Time    : 2018/5/3 0003 17:27 
  3. # @Author  : Langzi 
  4. # @Blog    : www.langzi.fun 
  5. # @File    : 面向对象5.py 
  6. # @Software: PyCharm 
  7. import sys 
  8. import requests 
  9. reload(sys) 
  10. sys.setdefaultencoding('utf-8') 
  11. class gg: 
  12.     url = 0 
  13.     stat = 0 
  14.     # 由于使用classmethod后会传入新的变量,因此一开始是须要本身先定义类变量 
  15.     def __init__(self,url=0,stat=0): 
  16.     # 这里按照正常的定义构造函数 
  17.         self.url=url 
  18.         self.stat=stat 
  19.     @classmethod 
  20.     # 装饰器,立马执行下面的函数 
  21.     def split(cls,info): 
  22.         # 这个函数接受两个参数,默认的cls就是这个类的init函数,info就是外面传入进来的 
  23.         url,stat=map(str,info.split('-')) 
  24.         # 这里转换成了格式化的结构 
  25.         data = cls(url,stat) 
  26.         # 而后执行这个类第一个方法,这个类构造函数须要传入两个参数,因而就传入了两个参数 
  27.         return data 
  28.         # 这里就直接返回了函数结果 
  29.     def outer(self): 
  30.         print self.url 
  31.         print self.stat 
  32.  
  33. r = gg.split(('langzi-200')) 
  34. r.outer() 
  35. # 这里是调用类方法,与调用实例方法同样 

类的特性

封装

封装是指将数据与具体操做的实现代码放在某个对象内部,外部没法访问。必需要先调用类的方法才能启动。

案例

 
  1. class cc: 
  2.     ccc = 'ccc' 
  3.     # cc就是类名 若是想要继承别的类 就class cc(threading) 意思就是从threading继承 
  4.     def __init__(self,a,b,c): 
  5.         self.a=a 
  6.         self.b=b 
  7.         self.c=c 
  8. print e.ccc 
  9. #类变量,在类里面找到定义的变量。 
  10. print ccc 
  11. # 这里会报错,这就是封装。类中的函数同理。 

继承

当咱们定义一个class的时候,能够从某个现有的class继承,新的class称为子类(Subclass),而被继承的class称为基类、父类或超类(Base class、Super class)。

好比,咱们已经编写了一个名为Animal的class,有一个run()方法能够直接打印:

 
  1. class Animal(object): 
  2.     def run(self): 
  3.         print 'Animal is running...' 

当咱们须要编写Dog和Cat类时,就能够直接从Animal类继承:

 
  1. class Dog(Animal): 
  2.     pass 
  3. class Cat(Animal): 
  4.     pass 

继承有什么好处?最大的好处是子类得到了父类的所有功能。因为Animial实现了run()方法,所以,Dog和Cat做为它的子类,什么事也没干,就自动拥有了run()方法:

 
  1. dog = Dog() 
  2. dog.run() 
  3. cat = Cat() 
  4. cat.run() 

当子类和父类都存在相同的run()方法时,咱们说,子类的run()覆盖了父类的run(),在代码运行的时候,老是会调用子类的run()。这样,咱们就得到了继承的另外一个好处:多态。

多态

要理解多态的好处,咱们还须要再编写一个函数,这个函数接受一个Animal类型的变量:

 
  1. def run_twice(animal): 
  2.     animal.run() 
  3.     animal.run() 

当咱们传入Animal的实例时,run_twice()就打印出:

 
  1. run_twice(Animal()) 
  2. 运行结果: 
  3. Animal is running... 
  4. Animal is running... 

当咱们传入Dog的实例时,run_twice()就打印出:

 
  1. run_twice(Dog()) 
  2. 运行结果: 
  3. Dog is running... 
  4. Dog is running... 

当咱们传入Cat的实例时,run_twice()就打印出:

 
  1. run_twice(Cat()) 
  2. 运行结果: 
  3. Cat is running... 
  4. Cat is running... 

看上去没啥意思,可是仔细想一想,如今,若是咱们再定义一个Tortoise类型,也从Animal派生:

 
  1. class Tortoise(Animal): 
  2.     def run(self): 
  3.         print 'Tortoise is running slowly...' 

当咱们调用run_twice()时,传入Tortoise的实例:

 
  1. run_twice(Tortoise()) 
  2. 运行结果: 
  3. Tortoise is running slowly... 
  4. Tortoise is running slowly... 

你会发现,新增一个Animal的子类,没必要对run_twice()作任何修改,实际上,任何依赖Animal做为参数的函数或者方法均可以不加修改地正常运行,缘由就在于多态。

多态的好处就是,当咱们须要传入Dog、Cat、Tortoise……时,咱们只须要接收Animal类型就能够了,由于Dog、Cat、Tortoise……都是Animal类型,而后,按照Animal类型进行操做便可。因为Animal类型有run()方法,所以,传入的任意类型,只要是Animal类或者子类,就会自动调用实际类型的run()方法,这就是多态的意思:

对于一个变量,咱们只须要知道它是Animal类型,无需确切地知道它的子类型,就能够放心地调用run()方法,而具体调用的run()方法是做用在Animal、Dog、Cat仍是Tortoise对象上,由运行时该对象的确切类型决定,这就是多态真正的威力:调用方只管调用,无论细节,而当咱们新增一种Animal的子类时,只要确保run()方法编写正确,不用管原来的代码是如何调用的。这就是著名的“开闭”原则:

对扩展开放:容许新增Animal子类;

对修改封闭:不须要修改依赖Animal类型的run_twice()等函数。

总结:继承能够把父类的全部功能都直接拿过来,这样就没必要重零作起,子类只须要新增本身特有的方法,也能够把父类不适合的方法覆盖重写;

有了继承,才能有多态。在调用类实例方法的时候,尽可能把变量视做父类类型,这样,全部子类类型均可以正常被接收;

旧的方式定义Python类容许不从object类继承,但这种编程方式已经严重不推荐使用。任什么时候候,若是没有合适的类能够继承,就继承自object类。

魔法方法

在上面有提到除了init以外还有iter,reverse的方法,这里就详细说下除了init初始化还有哪些别的方法。

 
  1. __init__ :      构造函数,在生成对象时调用 
  2. __del__ :       析构函数,释放对象时使用 
  3. __repr__ :      打印,转换 
  4. __setitem__ :   按照索引赋值 
  5. __getitem__:    按照索引获取值 
  6. __len__:        得到长度 
  7. __cmp__:        比较运算 
  8. __call__:       调用 
  9. __add__:        加运算 
  10. __sub__:        减运算 
  11. __mul__:        乘运算 
  12. __div__:        除运算 
  13. __mod__:        求余运算 
  14. __pow__:        幂 

具体使用

1. doc

说明性文档和信息。Python自建,无需自定义。

 
  1. class Foo: 
  2.     """ 描述类信息,可被自动收集 """ 
  3.     def func(self): 
  4.         pass 
  5. # 打印类的说明文档  
  6. print(Foo.__doc__) 

2. init()

实例化方法,经过类建立实例时,自动触发执行。

 
  1. class Foo: 
  2.     def __init__(self, name): 
  3.         self.name = name 
  4.         self.age = 18 
  5. obj = Foo(jack') # 自动执行类中的 __init__ 方法 

3. module__ 和 __class

module 表示当前操做的对象在属于哪一个模块。

class 表示当前操做的对象属于哪一个类。

这二者也是Python内建,无需自定义。

 
  1. class Foo: 
  2.     pass 
  3. obj = Foo() 
  4. print(obj.__module__) 
  5. print(obj.__class__) 

运行结果:

 
  1. main 

4. del()

析构方法,当对象在内存中被释放时,自动触发此方法。

注:此方法通常无须自定义,由于Python自带内存分配和释放机制,除非你须要在释放的时候指定作一些动做。析构函数的调用是由解释器在进行垃圾回收时自动触发执行的。

 
  1. class Foo: 
  2.     def __del__(self): 
  3.         print("我被回收了!") 
  4.  
  5. obj = Foo() 
  6. del obj 

5. call()

若是为一个类编写了该方法,那么在该类的实例后面加括号,可会调用这个方法。

注:构造方法的执行是由类加括号执行的,即:对象 = 类名(),而对于call() 方法,是由对象后加括号触发的,即:对象() 或者 类()()

 
  1. class Foo: 
  2.     def __init__(self): 
  3.         pass 
  4.     def __call__(self, *args, **kwargs): 
  5.         print('__call__') 
  6. obj = Foo()     # 执行 __init__ 
  7. obj()       # 执行 __call__ 

能够用Python内建的callable()函数进行测试,判断一个对象是否能够被执行。

 
  1. callable(Student()) 

运行结果:

 
  1. True 

6. dict

列出类或对象中的全部成员!很是重要和有用的一个属性,Python自建,无需用户本身定义。

 
  1. class Province: 
  2.     country = 'China' 
  3.     def __init__(self, name, count): 
  4.         self.name = name 
  5.         self.count = count 
  6.     def func(self, *args, **kwargs): 
  7.         print('func') 
  8. # 获取类的成员 
  9. print(Province.__dict__) 
  10. # 获取 对象obj1 的成员  
  11. obj1 = Province('HeBei',10000) 
  12. print(obj1.__dict__) 
  13. # 获取 对象obj2 的成员  
  14. obj2 = Province('HeNan', 3888) 
  15. print(obj2.__dict__) 

7. str()

若是一个类中定义了str()方法,那么在打印对象时,默认输出该方法的返回值。这也是一个很是重要的方法,须要用户本身定义。

下面的类,没有定义str()方法,打印结果是:

 
  1. class Foo: 
  2.     pass 
  3. obj = Foo() 
  4. print(obj) 
  5. 定义了__str__()方法后,打印结果是:'jack'。 
  6. class Foo: 
  7.     def __str__(self): 
  8.         return 'jack' 
  9. obj = Foo() 
  10. print(obj) 

八、getitem__()、_setitem_()、__delitem()

取值、赋值、删除这“三剑客”的套路,在Python中,咱们已经见过不少次了,好比前面的@property装饰器。

Python中,标识符后面加圆括号,一般表明执行或调用方法的意思。而在标识符后面加中括号[],一般表明取值的意思。Python设计了getitem()、setitem()、delitem()这三个特殊成员,用于执行与中括号有关的动做。它们分别表示取值、赋值、删除数据。

也就是以下的操做:

 
  1. a = 标识符[] : 执行__getitem__方法 
  2. 标识符[] = a  : 执行__setitem__方法 
  3. del 标识符[] : 执行__delitem__方法 

若是有一个类同时定义了这三个魔法方法,那么这个类的实例的行为看起来就像一个字典同样,以下例所示:

 
  1. class Foo: 
  2.     def __getitem__(self, key): 
  3.         print('__getitem__',key) 
  4.     def __setitem__(self, key, value): 
  5.         print('__setitem__',key,value) 
  6.     def __delitem__(self, key): 
  7.         print('__delitem__',key) 
  8. obj = Foo() 
  9. result = obj['k1']      # 自动触发执行 __getitem__ 
  10. obj['k2'] = 'jack'      # 自动触发执行 __setitem__ 
  11. del obj['k1']             # 自动触发执行 __delitem__ 

9. iter()

这是迭代器方法!列表、字典、元组之因此能够进行for循环,是由于其内部定义了 iter()这个方法。若是用户想让自定义的类的对象能够被迭代,那么就须要在类中定义这个方法,而且让该方法的返回值是一个可迭代的对象。当在代码中利用for循环遍历对象时,就会调用类的这个iter()方法。

普通的类:

 
  1. class Foo: 
  2.     pass 
  3. obj = Foo() 
  4. for i in obj: 
  5.     print(i) 
  6. # 报错:TypeError: 'Foo' object is not iterable<br># 缘由是Foo对象不可迭代 
  7. 添加一个__iter__(),但什么都不返回: 
  8. class Foo: 
  9.     def __iter__(self): 
  10.         pass 
  11. obj = Foo() 
  12. for i in obj: 
  13.     print(i) 
  14. # 报错:TypeError: iter() returned non-iterator of type 'NoneType' 
  15. #缘由是 __iter__方法没有返回一个可迭代的对象 

返回一个个迭代对象:

 
  1. class Foo: 
  2.     def __init__(self, sq): 
  3.         self.sq = sq 
  4.     def __iter__(self): 
  5.         return iter(self.sq) 
  6. obj = Foo([11,22,33,44]) 
  7. for i in obj: 
  8.     print(i) 

最好的方法是使用生成器:

 
  1. class Foo: 
  2.     def __init__(self): 
  3.         pass 
  4.     def __iter__(self): 
  5.         yield 1 
  6.         yield 2 
  7.         yield 3 
  8. obj = Foo() 
  9. for i in obj: 
  10.     print(i) 

十、len()

在Python中,若是你调用内置的len()函数试图获取一个对象的长度,在后台,实际上是去调用该对象的len()方法,因此,下面的代码是等价的:

 
  1. len('ABC') 
  2. 'ABC'.__len__() 

Python的list、dict、str等内置数据类型都实现了该方法,可是你自定义的类要实现len方法须要好好设计。

11. repr()

这个方法的做用和str()很像,二者的区别是str()返回用户看到的字符串,而repr()返回程序开发者看到的字符串,也就是说,repr()是为调试服务的。一般二者代码同样。

 
  1. class Foo: 
  2.     def __init__(self, name): 
  3.         self.name = name 
  4.     def __str__(self): 
  5.         return "this is %s" % self.name 
  6.     __repr__ = __str__ 

12. add__: 加运算 _sub_: 减运算 _mul_: 乘运算 _div_: 除运算 _mod_: 求余运算 __pow: 幂运算

这些都是算术运算方法,须要你本身为类设计具体运算代码。有些Python内置数据类型,好比int就带有这些方法。Python支持运算符的重载,也就是重写。

 
  1. class Vector: 
  2.    def __init__(self, a, b): 
  3.       self.a = a 
  4.       self.b = b 
  5.    def __str__(self): 
  6.       return 'Vector (%d, %d)' % (self.a, self.b) 
  7.    def __add__(self,other): 
  8.       return Vector(self.a + other.a, self.b + other.b) 
  9. v1 = Vector(2,10) 
  10. v2 = Vector(5,-2) 
  11. print (v1 + v2) 

13. author做者信息

 
  1. __author__ = "Jack" 
  2. def show(): 
  3.     print(__author__) 
  4. show() 

14. slots

Python做为一种动态语言,能够在类定义完成和实例化后,给类或者对象继续添加随意个数或者任意类型的变量或方法,这是动态语言的特性。例如:

 
  1. def print_doc(self): 
  2.     print("haha") 
  3.  
  4. class Foo: 
  5.     pass 
  6.  
  7. obj1 = Foo() 
  8. obj2 = Foo() 
  9. # 动态添加实例变量 
  10. obj1.name = "jack" 
  11. obj2.age = 18 
  12. # 动态的给类添加实例方法 
  13. Foo.show = print_doc 
  14. obj1.show() 
  15. obj2.show() 

可是!若是我想限制实例能够添加的变量怎么办?可使slots限制实例的变量,好比,只容许Foo的实例添加name和age属性。

 

 
  1. def print_doc(self): 
  2.     print("haha") 
  3. class Foo: 
  4.     __slots__ = ("name", "age") 
  5.     pass 
  6. obj1 = Foo() 
  7. obj2 = Foo() 
  8. # 动态添加实例变量 
  9. obj1.name = "jack" 
  10. obj2.age = 18 
  11. obj1.sex = "male"       # 这一句会弹出错误 
  12. # 可是没法限制给类添加方法 
  13. Foo.show = print_doc 
  14. obj1.show() 
  15. obj2.show() 
  16. 因为'sex'不在__slots__的列表中,因此不能绑定sex属性,试图绑定sex将获得AttributeError的错误。 
  17. Traceback (most recent call last): 
  18.   File "F:/Python/pycharm/201705/1.py", line 14, in <module> 
  19.     obj1.sex = "male" 
  20. AttributeError: 'Foo' object has no attribute 'sex' 

须要提醒的是,slots定义的属性仅对当前类的实例起做用,对继承了它的子类是不起做用的。想一想也是这个道理,若是你继承一个父类,却莫名其妙发现有些变量没法定义,那不是大问题么?若是非要子类也被限制,除非在子类中也定义slots,这样,子类实例容许定义的属性就是自身的slots加上父类的slots。

成员保护与访问机制

有些对象你不想外部访问,即便是经过调用类对象也没法访问,那就请认真学完本章节。

私有成员

 
  1. class obj: 
  2.     def __init__(self,name): 
  3.         self.name=name 
  4.     def pri(self): 
  5.         print self.name 
  6.     __age = 18 
  7.     # 加上双下划线的就是私有变量,只能在类的内部访问,外部没法访问 
  8. a = obj('zhao') 
  9. a.pri() 

运行结果:

 
  1. zhao 

若是要在类中调用这个私有成员,能够这么用

 
  1. class obj: 
  2.     def __init__(self,name): 
  3.         self.name=name 
  4.     def prin(self): 
  5.         print self.name 
  6.     __age = 18 
  7.     # 加上双下划线的就是私有变量,只能在类的内部访问,外部没法访问 
  8.     @classmethod 
  9.     # 若是要在类中调用,首先调用类方法 
  10.     def pri(cls): 
  11.         print cls.__age 
  12.         # 而后在使用 
  13. a = obj('zhao') 
  14. a.prin() 
  15. obj.pri() 
  16. # 经过这样直接调用类中的私有变量 

运行结果:

 
  1. zhao
  2. 18 

使用get-set-del方法操做私有成员

 
  1. class obj: 
  2.     def __init__(self,name): 
  3.         self.name=name 
  4.     def prin(self): 
  5.         print self.name 
  6.     __age = 18 
  7.     # 加上双下划线的就是私有变量,只能在类的内部访问,外部没法访问 
  8.     @classmethod 
  9.     # 若是要在类中调用,首先调用类方法 
  10.     def pri(cls): 
  11.         print cls.__age 
  12.         # 而后在使用 
  13.     @classmethod 
  14.     def set_age(cls,value): 
  15.         cls.__age = value 
  16.         return cls.__age 
  17.         # 这个用法就是改变__age的值 
  18.     @classmethod 
  19.     def get_age(cls): 
  20.         return cls.__age 
  21.         # 这个用法就是直接返回__age的值 
  22.     @classmethod 
  23.     def del_age(cls): 
  24.         del cls.__age 
  25.         # 这个用法就是直接删除__age的值 
  26.  
  27. print obj.get_age() 
  28. # 这里是直接调用出__age的值  返回值18 
  29. print obj.set_age(20) 
  30. # 这里是直接改变__age的值  返回值20 
  31. obj.del_age() 
  32. # 这里是直接删除__age的值 

思考: 既然是私有变量,不让外部访问,为什么有要在后面调用又改变呢?由于能够对私有变量进行额外的检测,处理,加工等等。好比判断value的值,使用isinstance而后作if-else判断。

使用私有变量能够对内部变量进行保护,外部没法改变,可是能够对它进行检测处理。

这里引伸一下私有成员的保护机制,使用__age对私有变量其实就是—>obj._obj__age的样子进行保护,说白了你直接使用obj._obj__age就能够直接调用内部私有变量age了。

Propety装饰器

把类的方法假装成属性调用的方式,就是把类里面的一个函数,变成一个属性同样的东西~

一开始调用类的方法要使用圆括号,如今变成了属性进行读取设置存储。

举个例子来讲明:

经常使用的调用方法

 
  1. class obj: 
  2.     def __init__(self,name,age): 
  3.         self.__name=name 
  4.         self.__age=age 
  5.         # 讲这些设置成私有变量 
  6.     def get_age(self): 
  7.         return self.__age 
  8.     def set_age(self,value): 
  9.         if isinstance(value,int): 
  10.             self.__age=value 
  11.         else: 
  12.             raise ValueError('非整数类型') 
  13.     def del_age(self): 
  14.         print 'delete over' 
  15. a = obj('langzi',18) 
  16. print a.get_age() 
  17. a.set_age(20) 
  18. print a.get_age() 

使用装饰器

 
  1. class obj: 
  2.     def __init__(self,name,age): 
  3.         self.__name=name 
  4.         self.__age=age 
  5.         # 把这些设置成私有变量 
  6.     @property 
  7.     def age(self): 
  8.         return self.__age 
  9.     @age.setter 
  10.     def age(self,value): 
  11.         if isinstance(value,int): 
  12.             self.__age=value 
  13.         else: 
  14.             raise ValueError('非整数类型') 
  15.     @age.deleter 
  16.     def age(self): 
  17.         print 'delete over' 
  18. a = obj('langzi',18) 
  19. # 使用这些装饰器,可使用类与对象的方法直接调用 
  20. print a.age 
  21. # 这里就是直接调用返回age的值 
  22. a.age=20 
  23. # 这里就是直接使用setter把值转换 
  24. print a.age 
  25. del a.age 
  26. # 删除age 

固然这种调用方法有些麻烦,每次都是一个一个去实例类与对象,有个更加简单直观的方法。

更加减半的使用property()函数

除了使用装饰器的方式将一个方法假装成属性外,Python内置的builtins模块中的property()函数,为咱们提供了第二种设置类属性的手段。

 
  1. class People: 
  2.  
  3.     def __init__(self, name, age): 
  4.         self.__name = name 
  5.         self.__age = age 
  6.  
  7.     def get_age(self): 
  8.         return self.__age 
  9.  
  10.     def set_age(self, age): 
  11.         if isinstance(age, int): 
  12.             self.__age = age 
  13.         else: 
  14.             raise ValueError 
  15.  
  16.     def del_age(self): 
  17.         print("删除年龄数据!") 
  18.  
  19.     # 核心在这句 
  20.     age = property(get_age, set_age, del_age, "年龄")     
  21.  
  22.  
  23. obj = People("jack", 18) 
  24. print(obj.age) 
  25. obj.age = 19 
  26. print("obj.age:  ", obj.age) 
  27. del obj.ag 

经过语句age = property(get_age, set_age, del_age, “年龄”)将一个方法假装成为属性。其效果和装饰器的方法是同样的。

property()函数的参数:

 
  1. 第一个参数是方法名,调用 实例.属性 时自动执行的方法 
  2. 第二个参数是方法名,调用 实例.属性 = XXX时自动执行的方法 
  3. 第三个参数是方法名,调用 del 实例.属性 时自动执行的方法 
  4. 第四个参数是字符串,调用 实例.属性.__doc__时的描述信息。 
相关文章
相关标签/搜索