zoukankan      html  css  js  c++  java
  • python advanced programming ( II )


    面向对象编程


    简称OOP,是一种程序设计思想。OOP把对象作为程序的基本单元,一个对象包含了数据和操作数据的函数。数据封装、继承和多态是面向对象的三大特点。 

    在Python中,所有数据类型都可以视为对象,当然也可以自定义对象。自定义的对象数据类型就是
    面向对象中的类(Class)的概念。

    给对象发消息实际上就是调用对象对应的关联函数,称之为对象的方法(Method)


    OOP最重要的概念就是(Class)和实例(Instance),牢记类是抽象的模板,而实例是根据类创建出来的一个个具体的“对象”,每个对象都拥有相同的方法,但各自的数据可能不同。(艾维铝酉和cjk区别——#)  

    定义类通过class关键字

    1 class Student(object):
    2     pass  
    3 
    4 #object表示该类是从哪个类继承下来的,如果没有合适的继承类就使用object类,这是所有类最终都会继承的类。

    创建实例通过类名+()

    1 bart = Student()                        

    可以给实例的变量绑定属性

    1 bart.name = 'Bart Simpson'

    由于类可以起到模板的作用,在创建实例的时候可以通过定义__init__方法把认为必要的属性强制填写进去绑定

    1 class Student(object):
    2  
    3      def __init__(self, name, score):   #将name和score属性绑定
    4          self.name = name
    5          self.score = score

    #__init__方法的第一个参数永远是self,表示创建的实例本身,因此可以把各种属性绑定到self,因为self就指向创建的实例本身。

    有了__init__方法,在创建实例的时候,就不能传入空的参数了而必须传入与__init__方法匹配的参数,self不需要传

    1 >>> bart = Student('Bart Simpson', 59)
    2 >>> bart.name
    3 'Bart Simpson'
    4 >>> bart.score
    5 59

    在类中定义的函数和普通函数相比只有一点不同,就是第一个参数永远是实例变量self,并且调用时不用传递该参数。

    访问实例的数据,没必要用外面的函数去访问,可以直接在类的内部定义访问数据的函数,这样,就把“数据”给封装起来了,称为数据封装。这些封装数据的函数是和类本身是关联起来的,称为类的方法

    1 class Student(object):
    2 
    3     def __init__(self, name, score):
    4         self.name = name
    5         self.score = score
    6 
    7     def print_score(self):
    8         print('%s: %s' % (self.name, self.score))  #调用方法>>>bart.print_score() 

    封装的一个好处是不用知道内部实现的细节容易调用,另一个是好处是可以给类增加新的方法

     1 class Student(object):
     2     ...
     3 
     4     def get_grade(self):              #给该类增加一个get_grade方法
     5         if self.score >= 90:
     6             return 'A'
     7         elif self.score >= 60:
     8             return 'B'
     9         else:
    10             return 'C'

    类是创建实例的模板,而实例则是一个一个具体的对象,各个实例拥有的数据都互相独立,互不影响;

    方法就是与实例绑定的函数,和普通函数不同,方法可以直接访问实例的数据(普通函数需要访问类);


    使用上述方法可以隐藏内部复杂逻辑,而在属性名称前加上两个下划线__可以访问限制,使得外部代码不能自由修改实例的属性

     1 class Student(object):
     2                                                               
     3     def __init__(self, name, score):         #之前>>> bart.score  98
     4                                              #   >>> bart.score = 59
     5                                              #   >>> bart.score   59
     6         self.__name = name
     7         self.__score = score                 #之后>>> bart.__name  Error
     8                                        
     9     def print_score(self):
    10         print('%s: %s' % (self.__name, self.__score))

    如果外部代码要获取属性,可以给类增加get_nameget_score这样的方法

    1 class Student(object):
    2     ...
    3 
    4     def get_name(self):
    5         return self.__name
    6 
    7     def get_score(self):
    8         return self.__score

    如果又要允许外部代码修改属性,可以再给类增加set_score方法

    1 class Student(object):
    2     ...
    3 
    4     def set_score(self, score):
    5         self.__score = score

    类似__xxx__是特殊变量,可以直接访问,但是不要定义这样的变量名

    一个下划线开头的变量,可以被外部访问,但是约定俗成视为私有变量,不要随意访问      

    两个下划线开头的变量,可以通过   实例名._类名__变量名   来访问(不同版本.后的可能不同)

    总的来说就是,Python本身没有任何机制阻止你干坏事,一切全靠自觉。


    OOP程序设计中定义一个class的时候,可以从某个现有的class继承,子类将获得了父类的全部功能(方法),也可以对子类增加方法。如果一个变量是子类的数据类型,那么它同时也是父类、爷类...类型,这就是多态

     1 class Animal(object):                    #编写一个名为Animal的class
     2     def run(self):
     3         print('Animal is running...')
     4 
     5 class Dog(Animal):                       #直接从Animal类继承
     6     pass
     7 
     8 运行
     9 dog = Dog()
    10 dog.run()
    11 Animal is running...
    12 
    13 
    14 class Dog(Animal):                      #对子类增加方法
    15 
    16     def run(self):
    17         print('Dog is running...')
    18
    19 运行
    20 Dog is running... #调用时子类会覆盖与父类定义同名的方法

    多态的好处

    def run_twice(animal):    #定义一个接收Animal类型的函数
        animal.run()          #调用函数时不管参数是父类Animal()其子类比如Dog(),实际调用的都是类中各自定义的叫 run 的方法,只要                            实参是形参的子类,无需修改函数
                                         

    注:

    如果需要传入Animal类型,对于静态语言(如JAVA)传入的对象必须是Animal类型或其子类,否则无法调用run方法。对于动态语言(如Python),则不一定需要传入Animal类型,只需要保证传入的对象有一个run()方法就可以了。


     获取对象信息

    判断对象类型:基本类型都可以用type(),指向函数或者类的变量也可以用type()判断,返回对应的Class类型。

                        判断class的类型,可以使用isinstance( ,)函数,返回布尔值。

    获得对象的所有属性和方法:可以使用dir()函数,返回一个包含字符串的list,

     1 >>> hasattr(obj, 'x')     # 有属性'x'吗?
     2 True
     3 >>> obj.x
     4 9
     5 >>> hasattr(obj, 'y')     # 有属性'y'吗?
     6 False
     7 >>> setattr(obj, 'y', 19)     # 设置一个属性'y'
     8 >>> hasattr(obj, 'y')     # 有属性'y'吗?
     9 True
    10 >>> getattr(obj, 'y')     # 获取属性'y'
    11 19
    12 >>> obj.y     # 获取属性'y'
    13 19
    14 >>> fn = getattr(obj, 'y') # 获取属性'y'并赋值到变量fn
    15 
    16 #通过内置的一系列函数,可以对任意一个Python对象进行剖析,拿到其内部的数据。

    注:

    由于Python是动态语言,根据类创建的实例可以任意绑定属性。


    给实例绑定属性

    1 class Student(object):               #通过self变量
    2     def __init__(self, name):
    3         self.name = name
    4 
    5 s = Student('Bob')                   #通过实例变量
    6 s.score = 90

    给类绑定属性

    1 class Student(object):       #直接在class中定义属性
    2     name = 'Student'

    实例属性类属性优先级高

    给实例绑定方法

    1 >>> def set_age(self, age): # 定义一个函数作为实例方法
    2 ...     self.age = age
    3 ...
    4 >>> from types import MethodType
    5 >>> s.set_age = MethodType(set_age, s) # 给实例绑定一个方法
    6 >>> s.set_age(25) # 调用实例方法
    7 >>> s.age # 测试结果
    8 25

    给类绑定方法,相当于给其所有实例绑定了方法

    1 >>> def set_score(self, score):        #定义一个函数并把它绑定给类
    2 ...     self.score = score
    3 ...
    4 >>> Student.set_score = set_score

    限制实例的属性,在定义class的时候定义一个特殊的__slots__变量,比如只允许实例Student添加添加name和age属性:

    1 class Student(object):
    2     __slots__ = ('name', 'age')     # 用tuple装允许绑定的属性名称
    3                                               

    如果暴露性地绑定属性(绑定在定义外),无法检查绑定的参数是否合法,可以通过内置方法来解决,然后通过python内置的@property装饰器将getter方法变成属性简单的使用,同时另一个装饰器@.setter把绑值setter方法变成属性。

     1 class Student(object):
     2 
     3     @property                           #编写获得值的方法并变成属性
     4     def score(self):
     5         return self._score
     6 
     7     @score.setter                       #编写带自检的设值(绑定属性)方法并变成属性
     8     def score(self, value):
     9         if not isinstance(value, int):
    10             raise ValueError('score must be an integer!')
    11         if value < 0 or value > 100:
    12             raise ValueError('score must between 0 ~ 100!')
    13         self._score = value

    如果只定义getter方法(@property),不定义setter方法(@.setter)就是一个只读属性

    注:

    动态绑定允许在程序运行的过程中动态给class加上功能,静态语言必须定义在class中。


    面向对象高级编程


    通过多重继承,一个子类就可以同时获得多个父类的所有功能。这种继承如果按树型层次继承是复杂度呈指数增长,所以采用称之为MixIn的设计。设计类的继承关系时,设计单一继承下来的主线,将需要混入的额外功能的父类名尾加MixIn,这样就可以优先考虑通过多重继承来组合多个MixIn的功能,而不是设计多层次的复杂的继承关系。

    EXP Python自带了TCPServerUDPServer这两类网络服务,而要同时服务多个用户就必须使用多进程或多线程模型,这两种模型由ForkingMixInThreadingMixIn提供。通过组合,就可以创造出合适的服务来。

    1 class MyTCPServer(TCPServer, ForkingMixIn):    #编写一个多进程模式的TCP服务
    2     pass
    3 
    4 class MyUDPServer(UDPServer, ThreadingMixIn):    #编写一个多线程模式的UDP服务
    5     pass

    注:

    只允许单一继承的语言(如Java)不能使用MixIn的设计。


    定制类

    枚举类:Enum可以把一组相关常量定义在一个class中,即用枚举类型定义一个class,每个常量都是class的一个唯一实例。

     1 from enum import Enum            #定义Monthe类型的枚举类
     2 
     3 Month = Enum('Month', ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'))                     #可以用Month.XXX引用一个常量
     4 
     5 
     6 for name, member in Month.__members__.items():    
     7     print(name, '=>', member, ',', member.value)
     8 #枚举所有成员,value属性则是自动赋给成员的int常量,默认从1开始计数。
     9 
    10 
    11 from enum import Enum, unique     #可以通过从Enum派生出自定义类更精确的控制枚举
    12 
    13 @unique                           #@unique装饰器可以帮助检查保证没有重复值。
    14 class Weekday(Enum):
    15     Sun = 0                       # Sun的value被设定为0
    16     Mon = 1
    17     Tue = 2
    18     Wed = 3
    19     Thu = 4
    20     Fri = 5
    21     Sat = 6

    type()函数既可以返回一个对象的类型,又可以创建出新的类型。

    要创建一个class对象,type()函数依次传入3个参数:

    1. class的名称;
    2. 继承的父类集合,注意Python支持多重继承,如果只有一个父类,别忘了tuple的单元素写法;
    3. class的方法名称与函数绑定,这里我们把函数fn绑定到方法名hello上。
    1 >>> def fn(self, name='world'): # 先定义函数
    2 ...     print('Hello, %s.' % name)
    3 ...
    4 >>> Hello = type('Hello', (object,), dict(hello=fn)) # 创建Hello class

    通过type()函数创建的类和直接写class是完全一样的,但是type()函数也允许我们动态创建出类来。

    元类:除了使用type()动态创建类以外,要控制类的创建行为,还可以使用metaclass,直译为元类,简单的解释就是:先定义metaclass,就可以创建类,最后创建实例,可以把类看成是metaclass创建出来的“实例”。

    metaclass是Python面向对象里最难理解,也是最难使用的魔术代码,它可以改变类创建时的行为,功能强大,但不常用,使用起来务必小心。

  • 相关阅读:
    http与https的区别以及https的加密原理
    android 网络之 httppost
    android 定时器与Canvas结合使用
    逛超市遇到问题了还是好心人多
    获取最新前十篇文章
    做菜 蒸鸡蛋
    使用ASP.NET 2.0 Profile存储用户信息_1[翻译] Level 200
    ajax 笔记不用刷新实现数据的分页显示 2 (上)
    在人力资源树当中,上级对下级员工信息查看修改操作。
    今年入冬于来的第一场雪
  • 原文地址:https://www.cnblogs.com/Real-Ying/p/6635040.html
Copyright © 2011-2022 走看看