首页 > 技术文章 > Python 面向对象

nopnog 2017-06-12 18:36 原文

本章内容     

  1、面向对象简介

  2、析构函数

  3、封装

  4、继承

  5、多态

  6、类方法

  7、特殊的成员方法 

  8、反射

  9、异常处理

  10、动态载入

 

面向对象简介  

  面向对象编程——Object Oriented Programming,简称OOP,是一种程序设计思想。OOP把对象作为程序的基本单元,一个对象包含了数据和操作数据的函数。

  面向过程的程序设计把计算机程序视为一系列的命令集合,即一组函数的顺序执行。为了简化程序设计,面向过程把函数继续切分为子函数,即把大块函数通过切割成小块函数来降低系统的复杂度。

      而面向对象的程序设计把计算机程序视为一组对象的集合,而每个对象都可以接收其他对象发过来的消息,并处理这些消息,计算机程序的执行就是一系列消息在各个对象之间传递。

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

 

  以Student类为例,在Python中,定义类是通过class关键字:

class Student(object):
   n = 3 #类变量
   age = 11 #类变量
def __init__(self, name, score,age): #构造函数,在实例化的时候做一些类的初始化工作 self.name
= name #实例变量(静态属性),作用域就是实例的本身 self.score = score
self.age = age def print_score(self): print(
'%s: %s' % (self.name, self.score))

  class后面紧接着是类名,即Student,类名通常是大写开头的单词,

  紧接着是(object),表示该类是从哪个类继承下来的,通常,如果没有合适的继承类,就使用object类,这是所有类最终都会继承的类。

  定义好了student类,就可以通过student类,创造出student的实例,

  定义好了Student类,就可以根据Student类创建出Student的实例,创建实例是通过类名+()实现的: 

>>> bart = Student()
>>> bart
<__main__.Student object at 0x10a67a590>
>>> Student
<class '__main__.Student'>

  变量bart指向的就是一个Student的object,后面的0x10a67a590是内存地址,每个object的地址都不一样,Student本身则是一个类。

  __init__()方法是可选的,如果不提供,Python 会给出默认的__init__方法

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

  有了__init__方法,在创建实例的时候,就不能传入空的参数了,必须传入与__init__方法匹配的参数,但self不需要传,Python解释器自己会把实例变量传进去

  和普通的函数相比,在类中定义的函数只有一点不同,就是第一个参数永远是实例变量self,并且,调用时,不用传递该参数。除此之外,类的方法和普通函数没有什么区别,所以,你仍然可以用默认参数、可变参数、关键字参数和命名关键字参数

 

  变量的取值范围

a1 = Student('python','99','20')
print('age,n',a1.age,a1.n)


#运行结果
age,n 20 3

#age取的是实例变量,n取的是类变量
#结论,取的时候先取实例变量,如果没有的话择取类变量

  增加类变量

##bart.high = '1.8m'
##bar.high
  '1.8m'

   删除类变量

del a1.name
print(a1.name)

#运行结果
AttributeError: 'Student' object has no attribute 'name'

  修改类变量

a1 = Student('python','99','20')
a1.n = 18

print('from a1 n',a1.n)

a2 = Student('c++','88','10')
print('from a2 n',a2.n)

#在a1中修改了n的值,那n2种会不会有影响呢?
#运行结果:
from a1 n :18
from a2 n :3

#实验证明,a1,a2各自有各自的内存空间,内存空间内存放着自己的变量,a1修改的是自己内存空间内部的,所以a2,不受影响。
#注意,这里的n如果是列表的话,a1修改,a2是能看到的,这里是有与list的类型有关

   问个问题,类变量的用途?

   当我听到这个问题的时候,有点懵逼,怎么总结呢?alex做的总结,--->大家公用的属性,定义一个就可以了,节省开销。这个场景应用与cn = 'china',如果14亿中国人都要放到构造函数中吗?当然不必要,直接一个类函数即可。

 

析构函数

   在实例释放、销毁的时候自动执行的,通常用于做一些收尾的工作,如关闭一些数据库链接,或关闭打开的一些临时文件

class Student(object):

    def __init__(self, name, score,age):   
        self.name = name          
        self.score = score
        self.age = age

    def print_score(self):
        print('%s: %s' % (self.name, self.score))

    def __del__(self):
        print('del -------')

a1 = Student('python','99','22')
del a1

上面的“__del__”就是一个析构函数了,当使用del 删除对象时,会调用他本身的析构函数,另外当对象在某个作用域中调用完毕,在跳出其作用域的同时析构函数也会被调用一次,这样可以用来释放内存空间

  __del__()也是可选的,如果不提供,则Python 会在后台提供默认析构函数

 

封装

   私有属性/方法

  私有属性参考 http://www.cnblogs.com/blackmatrix/p/5600830.html

  其实,Python并没有真正的私有化支持,但可用下划线得到伪私有。   尽量避免定义以下划线开头的变量!

   (1)_xxx      "单下划线 " 开始的成员变量叫做保护变量,意思是只有类实例和子类实例能访问到这些变量,需通过类提供的接口进行访问;不能用'from module import *'导入

        (2)__xxx    类中的私有变量/方法名 (Python的函数也是对象,所以成员方法称为成员变量也行得通。)," 双下划线 " 开始的是私有成员,意思是只有类对象自己能访问,连子类对象也不能访问到这个数据。

        (3)__xxx__ 系统定义名字,前后均有一个“双下划线” 代表python里特殊方法专用的标识,如 __init__()代表类的构造函数。  

 

继承

class People():
    def __init__(self,name,age):
        self.name = name
        self.age = age

    def talk(self):
        print('%s is talking ....' %self.name)

    def eat(self):
        print('%s is eating ....'%self.name)


class man(People):
    def strong(self):
        print('%s is strong'%self.name)


m1 = man('alex','22')
m1.strong()
m1.talk()

#运行结果
alex is strong
alex is talking ....

  这就实现了man对people的继承,能实现了继承,那能对父类的重构呢?

class People():
    def __init__(self,name,age):
        self.name = name
        self.age = age

    def talk(self):
        print('%s is talking ....' %self.name)

    def eat(self):
        print('%s is eating ....'%self.name)


class man(People):
    def strong(self):
        print('%s is strong'%self.name)
#  重构父类的talk方法        
    def talk(self):
        People.talk(self)
        print('man is talking...')

m1 = man('alex','22')
m1.strong()
m1.talk()

#运行结果
alex is strong
alex is talking ....
man is talking...
重构父类方法

如果类中想增加变量,而继承的父类中个数有限,那需要怎么办?那就重构构造函数

class People():
    def __init__(self,name,age):
        self.name = name
        self.age = age

    def talk(self):
        print('%s is talking ....' %self.name)

    def eat(self):
        print('%s is eating ....'%self.name)

class man(People):
    def __init__(self,name,age,hoby):
        #对构造函数重构的两种方法。
        #People.__init__(self,name,age)
        super(man,self).__init__(name,age)
        print('My hoby is %s'%hoby)

    def strong(self):
        print('%s is strong'%self.name)

    def talk(self):
        People.talk(self)
        print('man is talking...')

m1 = man('alex','22','basketball')
m1.strong()
m1.talk()

#运行结果
alex is strong
alex is talking ....
man is talking...
重构构造函数

  经典类&&新式类

  class People     : 经典类

  class People(object)     :新式类

      People.__init__(self,name,age)      :经典类

  super(man,self).__init__(name,age)     :新式类

    多重继承

class People():
    def __init__(self,name,age):
        self.name = name
        self.age = age

    def talk(self):
        print('%s is talking ....' %self.name)

    def eat(self):
        print('%s is eating ....'%self.name)

class relation():
    def makefriend(self,obj):
        print('%s is make friend with %s'%(self.name,obj.name))

#多重继承,继承People,与relation
class man(People,relation):
    def __init__(self,name,age,hoby):
        super(man,self).__init__(name,age)
        print('My hoby is %s'%hoby)

class woman(People):
    def born(self):
        print('%s born a body'%self.name)


m1 = man('alex','22','basketball')
w1 = woman('hanmeimei','21')

m1.makefriend(w1)
多重继承

    python2  经典类是按照深度优先来继承的,新式类是按照广度优先来继承的

  python3  都是通过广度优先来继承的

   测试是广度优先还是深度优先的方法:

__author__ = "Alex Li"

class A:
    def __init__(self):
        print("A")
class B(A):
    pass
    # def __init__(self):
    #     print("B")
class C(A):
    pass
    # def __init__(self):
    #     print("C")
class D(B,C):
    pass
    # def __init__(self):
    #     print("D")


obj = D()
测试方法

 

多态

  一种方法,多种实现  

class Animal:
    def __init__(self, name):  # Constructor of the class
        self.name = name

    def talk(self):  # Abstract method, defined by convention only
        pass #raise NotImplementedError("Subclass must implement abstract method")

    @staticmethod
    def animal_talk(obj):
        obj.talk()

class Cat(Animal):
    def talk(self):
        print('Meow!')


class Dog(Animal):
    def talk(self):
        print('Woof! Woof!')


d = Dog("陈荣华")
#d.talk()

c = Cat("徐良伟")
#c.talk()
#
# def animal_talk(obj):
#     obj.talk()

Animal.animal_talk(c)
Animal.animal_talk(d)
多态code

 

类方法

  静态方法

  通过@staticmethod装饰器即可把其装饰的方法变为一个静态方法,什么是静态方法呢?其实不难理解,普通的方法,可以在实例化后直接调用,并且在方法里可以通过self.调用实例变量或类变量,但静态方法是不可以访问实例变量或类变量的,一个不能访问实例变量和类变量的方法,其实相当于跟类本身已经没什么关系了,它与类唯一的关联就是需要通过类名来调用这个方法

class Dog(object):
    def __init__(self,name):
        self.name = name

    @staticmethod      #把eat方法变为静态方法
    def eat(self):
        print("%s is eating"% self.name)


d1 = Dog('puppy')
d1.eat()


#改变为静态方法后,在运行的话就会有个错误
#TypeError: eat() missing 1 required positional argument: 'self'

解决办法:
1. 调用时主动传递实例本身给eat方法,即d.eat(d) 
2. 在eat方法中去掉self参数,但这也意味着,在eat中不能通过self.调用实例中的其它变量了

   类方法

   类方法通过@classmethod装饰器实现,类方法和普通方法的区别是, 类方法只能访问类变量,不能访问实例变量

class Dog(object):

    #name = '我是类变量'
    def __init__(self,name):
        self.name = name

    @classmethod     #使之成为类方法
    def eat(self):
        print("%s is eating"% self.name)


d1 = Dog('puppy')
d1.eat()


#运行的时候会有个报错
#AttributeError: type object 'Dog' has no attribute 'name'

解决方法:
如果把上面的类变量打开,那就问题了

  属性方法

  属性方法的作用就是通过@property把一个方法变成一个静态属性

class Dog(object):

    def __init__(self,name):
        self.name = name

    @property          #使之变为属性方法
    def eat(self):
        print("%s is eating"% self.name)


d1 = Dog('puppy')
d1.eat()
#d1.eat

#运行结果会有报错
#TypeError: 'NoneType' object is not callable

解决办法:
直接d1.eat   来调用

  属性方法中的增加变量,删除属性

class Dog(object):

    def __init__(self,name):
        self.name = name
        self.__age = None
    @property          #使之变为属性方法
    def eat(self):
        print("%s is eating"% self.name,self.__age)

    @eat.setter       #属性方法中,再添加参数
    def eat(self,age):
        print("set age to " ,age)
        self.__age= age   #对值进行保存

    @eat.deleter     #删除属性的方法
    def eat(self):
        del d1.__age
        print("删除完毕!")

d1 = Dog('puppy')

d1.eat
d1.eat = '11'      #对属性进行复制
d1.eat

del d1.eat      #如果不添加eat.deleter ,直接调用这个方法会报错的

  属性方法的应用场景

class Flight(object):
    def __init__(self,name):
        self.flight_name = name

    def checking_status(self):
        print("checking flight %s status " % self.flight_name)
        return  1

    @property
    def flight_status(self):
        status = self.checking_status()
        if status == 0 :
            print("flight got canceled...")
        elif status == 1 :
            print("flight is arrived...")
        elif status == 2:
            print("flight has departured already...")
        else:
            print("cannot confirm the flight status...,please check later")


f = Flight("CA980")
f.flight_status

#直接运行f.flight_status 就可以看到接口的查询结果了,相当于内部封装好,外部直接调用,和闭包很相像
调用航空的机票查询接口

 

类的特殊成员方法

  1、__doc__  表示类的描述信息

class Foo:
    """ 描述类信息,这是用于看片的神奇 """
 
    def func(self):
        pass
 
print Foo.__doc__
#输出:类的描述信息,这个注释必须放到类名的下面
View Code

  2、__module__和__class__

#放到包下
class C:

    def __init__(self):
        self.name = 'wupeiqi'


#在包外面
from lib.aa import C

obj = C()
print obj.__module__  # 输出 lib.aa,即:输出模块
print obj.__class__      # 输出 lib.aa.C,即:输出类
View Code

  3、__init__ 构造方法,通过类创建对象时,自动触发执行。

  4、__del__析构方法,当对象在内存中被释放时,自动触发执行。


注:此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的

  5、__call__ 对象后面加括号,触发执行。


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

class Foo:
 
    def __init__(self):
        pass
     
    def __call__(self, *args, **kwargs):
 
        print '__call__'
 
 
obj = Foo() # 执行 __init__
obj()       # 执行 __call__
View Code

  6. __dict__ 查看类或对象中的所有成员

class Province:
    country = 'China'

    def __init__(self, name, count):
        self.name = name
        self.count = count

    def func(self, *args, **kwargs):
        print
        'func'


# 获取类的成员,即:静态字段、方法、
print(Province.__dict__)
# 输出:{'country': 'China', '__module__': '__main__', 'func': <function func at 0x10be30f50>, '__init__': <function __init__ at 0x10be30ed8>, '__doc__': None}

obj1 = Province('HeBei', 10000)
print(obj1.__dict__)
# 获取 对象obj1 的成员
# 输出:{'count': 10000, 'name': 'HeBei'}

obj2 = Province('HeNan', 3888)
print(obj2.__dict__)
# 获取 对象obj1 的成员
# 输出:{'count': 3888, 'name': 'HeNan'}
View Code

  7、__str__ 如果一个类中定义了__str__方法,那么在打印 对象 时,默认输出该方法的返回值。

class Foo:
 
    def __str__(self):
        return 'alex li'
 
 
obj = Foo()
print obj
# 输出:alex li
View Code

  8、__getitem__、__setitem__、__delitem__

用于索引操作,如字典。以上分别表示获取、设置、删除数据,有啥用?还不知道

class Foo(object):
 
    def __getitem__(self, key):
        print('__getitem__',key)
 
    def __setitem__(self, key, value):
        print('__setitem__',key,value)
 
    def __delitem__(self, key):
        print('__delitem__',key)
 
 
obj = Foo()
 
result = obj['k1']      # 自动触发执行 __getitem__
obj['k2'] = 'alex'   # 自动触发执行 __setitem__
del obj['k1'] 
View Code

  9、__new__ \ __metaclass__

class Foo(object):
 
    def __init__(self,name):
        self.name = name
 
f = Foo("alex")

print type(f) #输出:<class '__main__.Foo'> 表示,obj 对象由Foo类创建
print type(Foo) #输出:<type 'type'> 表示,Foo类对象由 type类创建

上述代码中,obj 是通过 Foo 类实例化的对象,其实,不仅 obj 是一个对象,Foo类本身也是一个对象,因为在Python中一切事物都是对象。如果按照一切事物都是对象的理论:obj对象是通过执行Foo类的构造方法创建,那么Foo类对象应该也是通过执行某个类的 构造方法 创建。

  所以,f对象是Foo类的一个实例Foo类对象是 type 类的一个实例,即:Foo类对象 是通过type类的构造方法创建。那么,创建类就可以有两种方式:

  1、普通方法

class Foo(object):
  
    def func(self):
        print 'hello alex'

  2、特殊方法

def func(self):
    print 'hello wupeiqi'
  
Foo = type('Foo',(object,), {'func': func})
#type第一个参数:类名
#type第二个参数:当前类的基类
#type第三个参数:类的成员

  特殊方法加上构造函数

def func(self):
    print("hello %s"%self.name)

def __init__(self,name,age):
    self.name = name
    self.age = age
Foo = type('Foo',(object,),{'func':func,'__init__':__init__})

f = Foo("jack",22)
f.func()
特殊方法加上构造函数

 

反射

  反射即想到4个内置函数分别为:getattr、hasattr、setattr、delattr  获取成员、检查成员、设置成员、删除成员下面逐一介绍先看例子:

class Foo(object):
    def __init__(self):
        self.name = 'abc'

    def func(self):
        return 'ok'


obj = Foo()

#获取并执行成员
res = getattr(obj,'func')()
print(res)
#运行结果:ok

#检查成员
res = hasattr(obj,'func')
print(res)
#运行结果,因为有func,返回True

#设置成员值
print(obj.name)
res = setattr(obj,'name',19)
print(obj.name)
#运行结果:
abc
19

#删除成员
print(obj.name)
delattr(obj,'name')
print(obj.name)
#运行结果
#AttributeError: 'Foo' object has no attribute 'name'

  如果setattr没有这个属性,那如何添加

class Foo(object):
    def __init__(self,name):
        self.name = name

    def func(self):
        return 'ok'


def talk(self):
    print('%s is talking ...'%self.name)
obj = Foo('Python')

str = 'buck'
res = setattr(obj,str,None)
print(res)
#如果第三位指定None或者其他字符,则可以直接返回

setattr(obj,str,talk)
res = getattr(obj,str)
res(obj)
#如果第三位指定的是个函数,那需要用上面的这个方法

 

异常处理

  在编程过程中为了增加友好性,在程序出现bug时一般不会将错误信息显示给用户,而是现实一个提示的页面,通俗来说就是不让用户看见大黄页!!!

python2
try:
    pass
except Exception,ex:
    pass

python3
try:
    pass
except Exception as ex:     #万能异常,捕获所有的异常
    pass

终极版
try:
  pass
except (aerror,berror) as e:
  pass
except cerror as e:
  pass
else:
  print('没有错误,则我执行,一切正常')
finally:
  print('无论什么情况我都会执行')

  主动出发异常

try:
    raise Exception('错误了。。。')
except Exception,e:
    print e

  自定义异常 

class WupeiqiException(Exception):
 
    def __init__(self, msg):
        self.message = msg
 
    def __str__(self):
        return self.message
 
try:
    raise WupeiqiException('我的异常')
except WupeiqiException,e:
    print e

  断言

# assert 条件
 
assert 1 == 1
 
assert 1 == 2


用断言判断前面的条件,只有断言的条件正确,才执行后面的程

   需要注意的是,有些错误是捕捉不到的,如语法错误,缩进错误,导致pythno还没解析到捕捉错误的代码处就停止了,所以就不会有后面的捕捉动作了。

 

 动态载入

    动态引入,已字符串的形式引入。

     

 

   动态导入这个模块,两种方式

   1、lib = __import__('lib.aa')

       lib.aa.C().aa       #就可以实现调用了

  2、import importlib

     lib = importlib.import_modoule('lib.aa')

           lib.C().name          #这样就可以调用了,这是建议用法,比上面多引入了一层

 

 

 

返回python目录 

 

  

推荐阅读