三. python面向对象

第七章.面向对象基础

1.面向对象基础

类和对象:

  a. 创建类

class 类名:
    def 方法名(self,xxx):
        pass

  b. 创建对象

对象 = 类名()

  c. 通过对象执行方法

对象.方法名(123)

什么时候用面向对象?

  当某一些函数具有相同参数时,可以使用面向对象的方式,将参数值一次性的封装到对象,以后去对象中取值即可  

# 1.函数式编程
def mail(email,message):
    print("发送邮件")
    print(email)
    print(message)
    return True

mail("zgfraymond@163.com","hello")   # 两个实参分别传递给两个形参email,message

输出结果:
发送邮件
zgfraymond@163.com
hello
函数式编程
# 2.面向对象编程
class Foo:
    def mail(self,email, message):  # self参数:系统默认会把obj对象以及其内部封装的数据传递给self
        print("发送邮件")
        print(email)
        print(message)
        return True

obj = Foo()                             # 调用方法:1.用类名 () 创建对象;
obj.mail("zgfraymond@163.com","hello")  # 2. 通过对象去执行方法

输出结果:
发送邮件
zgfraymond@163.com
hello
面向对象编程

2.面向对象编程和函数式编程对比

# 函数编程
def fetch(host,username,password,sql):
    pass
def create(host,username,password,sql):
    pass
def rename(host,username,password,nid):
    pass
def modify(host,username,password,name):
    pass
fetch(...)
# -----------------------------------------------------------------------------------
# 面向对象
class SQLHelper:
    def fetch(self,sql):    # 此方法可去对象obj里取值:hhost,uusername,pwd;
        pass
    def create(self,sql):
        pass
    def rename(self,sql):
        pass
    def modify(self,sql):
        pass

obj = SQLHelper()   # 创建类对象obj

obj.hhost = "c1.salt.com"    # obj通过类对象指针去类里找到方法fetch,并去执行。
obj.uusername = "raymond"    # 可以把类方法中相同的参数封装在对象里面,类方法里可以去掉那些相同的参数
obj.pwd = "123"              # 此处obj对象内部封装了三条数据

obj.fetch("select * from A")
View Code

3.面向对象中对象和类的关系 

# 面向对象中对象和类的关系
class SQLHelper:
    def fetch(self,sql):    # 哪个对象调用类方法,方法中参数self就代表哪个对象,包括对象中封装的数据
        pass
    def create(self,sql):
        pass
    def rename(self,sql):
        pass
    def modify(self,sql):
        pass

obj1 = SQLHelper()          # obj1对象内部封装了三条数据
obj1.hhost = "c1.salt.com"
obj1.uusername = "raymond"
obj1.pwd = "123"
obj1.fetch("select * from A")  # 调用类方法fetch时,fetch方法中参数self就代表obj1对象

obj2 = SQLHelper()          # obj1对象内部封装了三条数据
obj2.hhost = "c2.salt.com"
obj2.uusername = "zgf"
obj2.pwd = "456"
obj2.fetch("select * from A")   # 调用类方法fetch时,fetch方法中参数self就代表obj2对象
View Code

4.面向对象之构造方法

class SQLHelper:
    # 下面的self参数是一个python自动会给传值的参数,哪个创建的对象去执行类方法,self就代表哪个对象
    def __init__(self,a1,a2,a3):    # 类中有一个特殊方法__init__(),类()自动执行该方法,被称为构造方法;开发中常用于封装数据
        print("当类创建对象时,自动执行init构造方法")
        self.hhost = a1
        self.uusername = a2
        self.pwd = a3
       
    def fetch(self,sql):    # 此方法可去对象obj里取值:hhost,uusername,pwd;
        pass
    def create(self,sql):
        pass
    def rename(self,sql):
        pass
    def modify(self,sql):
        pass

obj1 = SQLHelper('c1,selt.com','raymond',123)   # 对象内部封装参数传递给上面的a1,a2,a3
obj2 = SQLHelper('c2.selt.com','zgf',456)
View Code
View Code

5.面向对象之应用场景实例

综上所述,对于面向对象的封装来说,其实就是使用构造方法将内容封装到 对象中,然后通过对象直接或者self间接获取被封装的内容。

练习一:在终端输出如下信息

  • 小明,10岁,男,上山去砍柴
  • 小明,10岁,男,开车去东北
  • 小明,10岁,男,最爱大保健
  • 老李,90岁,男,上山去砍柴
  • 老李,90岁,男,开车去东北
  • 老李,90岁,男,最爱大保健
  • 老张...
def kanchai(name, age, gender):
    print "%s,%s岁,%s,上山去砍柴" %(name, age, gender)


def qudongbei(name, age, gender):
    print "%s,%s岁,%s,开车去东北" %(name, age, gender)


def dabaojian(name, age, gender):
    print "%s,%s岁,%s,最爱大保健" %(name, age, gender)


kanchai('小明', 10, '')
qudongbei('小明', 10, '')
dabaojian('小明', 10, '')


kanchai('老李', 90, '')
qudongbei('老李', 90, '')
dabaojian('老李', 90, '')

函数式编程
函数式编程
class Foo:
    
    def __init__(self, name, age ,gender):
        self.name = name
        self.age = age
        self.gender = gender

    def kanchai(self):
        print "%s,%s岁,%s,上山去砍柴" %(self.name, self.age, self.gender)

    def qudongbei(self):
        print "%s,%s岁,%s,开车去东北" %(self.name, self.age, self.gender)

    def dabaojian(self):
        print "%s,%s岁,%s,最爱大保健" %(self.name, self.age, self.gender)


xiaoming = Foo('小明', 10, '')
xiaoming.kanchai()
xiaoming.qudongbei()
xiaoming.dabaojian()

laoli = Foo('老李', 90, '')
laoli.kanchai()
laoli.qudongbei()
laoli.dabaojian()

面向对象
面向对象编程

上述对比可以看出,如果使用函数式编程,需要在每次执行函数时传入相同的参数,如果参数多的话,又需要粘贴复制了...  ;而对于面向对象只需要在创建对象时,将所有需要的参数封装到当前对象中,之后再次使用时,通过self间接去当前对象中取值即可。 

 

练习二:游戏人生程序

1、创建三个游戏人物,分别是:

  • 苍井井,女,18,初始战斗力1000
  • 东尼木木,男,20,初始战斗力1800
  • 波多多,女,19,初始战斗力2500

2、游戏场景,分别:

  • 草丛战斗,消耗200战斗力
  • 自我修炼,增长100战斗力
  • 多人游戏,消耗500战斗力
# -*- coding:utf-8 -*-

# #####################  定义实现功能的类  #####################

class Person:

    def __init__(self, na, gen, age, fig):
        self.name = na
        self.gender = gen
        self.age = age
        self.fight =fig

    def grassland(self):
        """注释:草丛战斗,消耗200战斗力"""

        self.fight = self.fight - 200

    def practice(self):
        """注释:自我修炼,增长100战斗力"""
        
        self.fight = self.fight + 200

    def incest(self):
        """注释:多人游戏,消耗500战斗力"""
        
        self.fight = self.fight - 500

    def detail(self):
        """注释:当前对象的详细情况"""

        temp = "姓名:%s ; 性别:%s ; 年龄:%s ; 战斗力:%s"  % (self.name, self.gender, self.age, self.fight)
        print temp

        
# #####################  开始游戏  #####################

cang = Person('苍井井', '', 18, 1000)    # 创建苍井井角色
dong = Person('东尼木木', '', 20, 1800)  # 创建东尼木木角色
bo = Person('波多多', '', 19, 2500)      # 创建波多多角色

cang.incest() #苍井空参加一次多人游戏
dong.practice()#东尼木木自我修炼了一次
bo.grassland() #波多多参加一次草丛战斗


#输出当前所有人的详细情况
cang.detail()
dong.detail()
bo.detail()


cang.incest() #苍井空又参加一次多人游戏
dong.incest() #东尼木木也参加了一个多人游戏
bo.practice() #波多多自我修炼了一次

#输出当前所有人的详细情况
cang.detail()
dong.detail()
bo.detail()

游戏人生
游戏人生

6.面向对象之对象中封装对象

下面示例画对象嵌套图会更好理解

对象中封装对象一
class c1:
    def __init__(self,name,obj):  # obj接收c2_obj对象作为参数
        self.name = name
        self.obj = obj

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

    def show(self):
        print(self.name)


c2_obj = c2("aa",11)    # c2类创建对象
c2_obj.show()           # 对象调用show方法

c1_obj = c1('zgf',c2_obj)  # 此处把c2_obj传递给c1类的obj参数
print(c1_obj.obj.name)      # 所以此处c1_obj.obj就表示c2_obj
print(c1_obj.name)

输出结果:
aa
aa
zgf
# ------------------------------------------------------------------------------------------
# 对象中封装对象二
class c1:
    def __init__(self,name,obj):  # obj接收c2_obj对象作为参数
        self.name = name
        self.obj = obj

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

    def show(self):
        print(self.name)
        return 123

class c3:
    def __init__(self,a1):
        self.money = 123
        self.aaa = a1

# 下面是三层对象嵌套,c3_obj嵌套c1_obj,  c1_obj1再嵌套c2_obj
c2_obj = c2("aa",11)    # c2类创建对象
# c2_obj是c2类型;内部封装数据:name = "aa"和 age = 11 ;

c1_obj = c1("alex",c2_obj)  # 此处把c2_obj传递给c1类的obj参数
# c1_obj是c1类型;内部封装数据:name = "alex" 和 obj = c2_obj

c3_obj = c3(c1_obj)
ret = c3_obj.aaa.obj.show()       # 使用c3_obj对象执行show(),并把show()方法中的返回值赋值给ret;如果没有,则输出None
print(ret)          # 此处输出show()中的返回值
print(c3_obj.aaa.obj.name)  # 使用c3_obj对象寻找c2类中的 name = "aa"

输出结果:
aa
aa
View Code

7.面向对象之单继承 

# 单继承
class F1:       # 父类 <==> 基类
    def show(self):
        print("show")
    def foo(self):
        print(self.name)

class F2(F1):       # 子类 <==> 派生类   相当于把F1父类里的方法添加到F2子类里
    def __init__(self,name):
        self.name = name

    def bar(self):
        print("bar")
    def show(self):
        print("F2.show")


obj = F2('zgf')
obj.show()      # 优先执行自身里的show()
obj.foo()       # 执行父类里的方法

输出结果:
F2.show
zgf
# ---------------------------------------------------------------------
class S1:
    def f1(self):
        self.f2()
    def f2(self):
        pass

class S2(S1):
    def f3(self):
        self.f1()
    def f2(self):
        pass

obj = S2()
obj.f2()    # 优先执行S2对象里的f2()
obj.f3()    # 先执行S1中f1(); 在父子类有相同方法时,优先执行自身类里的f2()


obj = S1()
obj.f1()    # 先执行S1中f1(),再执行自身S1中的f1()

# 由哪个类创建的对象,就优先执行哪个类对象里的方法
View Code

8.面向对象之多继承

                                                                     图1                                                                                              图2

 

# 多继承:只有python支持多继承;java,c#不支持
class C1:
    def f2(self):
        print('c1')

class C2:
    def f2(self):
        print('c2')

class C3(C2,C1):    # 继承关系里,左边C2优先级高于右边C1,所以优先执行C2类里的f2()
    def f3(self):   # 如果此处把f3(self)改为f2(self),则优先执行当前类里的f2()
        pass

obj = C3()
obj.f2()

#输出结果:c2
# ------------------------------------------------------------------------------------
# 本示例继承关系执行顺序见图1
class C0:
    def f2(self):
        print('c0')

class C1(C0):
    def f1(self):
        print('c1')

class C2:
    def f2(self):
        print('c2')

class C3(C1,C2):
    def f3(self):   # 如果此处把f3(self)改为f2(self),则优先执行当前类里的f2()
        pass

obj = C3()
obj.f2()
# 继承关系里,左边C1优先级高于右边C2,所以优先在C1类里寻找f2(),没有继续在C1的父类C0里寻找,找到便执行,没有再去C2里找,找到便执行
#  C2类里也有f2() 时,执行C0里的f2(),左边优先原则,所以输出结果如下:
# 输出结果:c0
# ------------------------------------------------------------------------------------
# 本示例继承关系执行顺序见图2
class C_2:
    def f2(self):
        print('C-2')

class C_1(C_2):
    def f2(self):
        print('C-1')

class C0(C_2):
    def f1(self):
        print('c0')

class C1(C0):
    def f1(self):
        print('c1')

class C2(C_1):
    def f1(self):
        print('c2')

class C3(C1,C2):
    def f3(self):   # 如果此处把f3(self)改为f2(self),则优先执行当前类里的f2()
        pass

obj = C3()
obj.f2()

#输出结果:C-1
多继承执行顺序

9.面向对象三大特性之多态

10.类成员知识讲解

类成员包括:

 字段

  • 静态字段:   代码加载时已经创建;类和对象都可以访问
  • 普通(动态)字段: 只有对象被创建后,只能通过对象去访问

 方法

  • 静态方法:可以有0或任意个参数,类和对象都可以调用执行,一般不使用对象调用
  • 普通(动态)方法:至少需要一个self参数,只有对象可以调用执行
  • 类方法:由类执行,至少需要一个 cls参数,类和对象都可以调用执行,一般不使用对象调用

 属性

  • 类成员属性的三种装饰器:@property ;@all_pager.setter ;@all_pager.deleter
  • 以上三种装饰器方法均伪造了对象访问普通字段的操作形式。该属性看上去不伦不类,具有方法的书写形式,具有字段的访问形式
  • 三种方法与对象的调用是一种固定形式的对应
  • 属性方法里可以实现任何的功能,不只是取值,赋值和删除值操作
  • 类成员之静态字段和普通字段 

# 静态字段和动态(普通)字段
class Foo:
    cc = 123    # 静态字段,保存在类里
    def __init__(self):
        self.name = 'zgf'   # 普通(动态)字段,保存在对象里
    def show(self):
        print(self.name)
# ------------------------------------------------------------------------------------
class province:
    country = "中国"      # 静态字段,代码加载时已经创建;类和对象都可以访问;多数情况用类访问,不建议使用对象访问
    def __init__(self,name):
        self.name = name    # 动态(普通)字段,只有对象可以访问,没创建对象是不可以访问的

hn = province("河南")
print(hn.name)      # 只有创建好对象,才可以访问动态字段
hb = province("河北")
print(hb.name)
sd = province("山东")
print(sd.name)
db = province("黑龙江")
print(db.name)

print(province.country)     # 应为静态字段保存在类里,所以可通过类名直接访问静态字段
print(hn.country)           # 对象也可以访问静态字段,但不建议使用该访问方法

#输出结果:
'''
河南
河北
山东
黑龙江
中国
中国
'''
View Code
  • 类成员之普通方法和静态方法以及类方法

# 静态方法和普通方法
class province:
    country = "中国"
    def __init__(self,name):
        self.name = name

    def show(self):      # 普通(动态)方法,只能由对象调用去执行方法(方法属于类),至少要有一个self参数
        print(self.name)
        print("show")

    @staticmethod          # 静态方法等同于python的函数,但与类相关联
    def f1(arg1,arg2):     # 把方法里的self去掉,上面加 @staticmethod 关键字,则此方法为静态方法,可以有0或任意个参数
        print("静态方法")
        print(arg1,arg2)

    @classmethod         # 类方法,至少要有一个参数cls(class)
    def f2(cls):
        print(cls)       # 输出类名province
       # cls()           # 此处利用类名(),可以创建对象
        print("类方法")

province.f1(111,222)     # 无需创建对象,可直接通过类去调用静态方法,并传递参数;优点:省内存
province.f2()            # 调用该方法时,不用传值,python自动把类名传递给类方法的cls参数

obj = province("河南")   # 创建对象去调用方法,消耗内存
obj.show()
obj.f1(333,444)          # 也可使用创建的对象去调用静态方法,并传递参数

# 输出结果:
'''
静态方法
111 222
河南
静态方法
333 444
'''
View Code
  • 类成员之属性  
# 类成员之属性表达形式一
class Pager:
    def __init__(self,all_count):
        self.all_count = all_count

    @property       # 属性装饰的方法,可通过对象访问不带括号的方法,取到方法中return的返回值   (功能:取值)
    def all_pager(self):
        a1,a2 = divmod(self.all_count,10)   # a1是商,a2是余数;应用场景:分页显示数据
        if a2 == 0:
            return a1
        else:
            return a1+1

    @all_pager.setter   # 此处为属性装饰的方法名加 .setter , 则可通过下面的类对象调用该方法,而且不需加括号,并且可赋值
    def all_pager(self,value):   # (功能:赋值)
        print(value)

    @all_pager.deleter # 此处为属性装饰的方法名加 .deleter ,则可通过下面 del p.all_pager调用该方法 (功能:删除值)
    def all_pager(self):
        print("del all_pager")


p = Pager(101)       # 创建对象,并给字段传值
print("--------------以下输出结果表示对象对普通字段常用的操作形式------------------")
print(p.all_count)   # 输出值为101
p.all_count = 102    # 给字段重新赋值
print(p.all_count)   # 重新赋值为102
# del p.all_count    # 删除字段,此处执行后,下栏的代码便报错;注释后可正常输出结果;说明此代码删除了对象封装的数据
print("--------------以下输出结果调用了@property 装饰器方法--------------------")
ret = p.all_pager    # 此处自动调用具有@property 装饰器装饰的方法,对象调用此方法不需加(),进行取值
print(ret)           # 此处ret的值就是调用方法的return返回值
print("--------------以下输出结果调用了@all_pager.setter  装饰器方法-----------")
p.all_pager = 111    # 此处自动调用具有@all_pager.setter 装饰器装饰的方法,对象调用此方法不需加(),进行赋值
print("--------------以下输出结果调用了@all_pager.deleter 装饰器方法 ----------")
del p.all_pager      # 此处自动调用具有@all_pager.deleter 装饰器装饰的方法,不需加(),进行删除值

# 总结如下:
# 1. 以上三种装饰器方法均伪造了对象访问普通字段的操作形式。该属性看上去不伦不类,具有方法的书写形式,具有字段的访问形式
# 2. 三种方法与对象的调用是一种固定形式的对应
# 3. 属性方法里可以实现任何的功能,不只是取值,赋值和删除值操作



# 输出结果:
'''
--------------以下输出结果表示对普通字段常用的操作形式------------------
101
102
--------------以下输出结果调用了@property 装饰器方法--------------------
11
--------------以下输出结果调用了@all_pager.setter  装饰器方法-----------
111
--------------以下输出结果调用了@all_pager.deleter 装饰器方法 ----------
del all_pager

'''
# -----------------------------------------------------------------------------------
# 类成员之属性表达形式二
class Pager:
    def __init__(self,all_count):
        self.all_count = all_count

    def f1(self):
        return 123

    def f2(self,value):
        print(value)

    def f3(self):
        print("del")


    foo = property(fget=f1,fset=f2,fdel=f3)     # 此处参数是固定形式写法,等同于下面的注释语句
    #foo = property(f1,f2,f3)    # 可以写的更简单一些


p = Pager(101)

ret = p.foo   # 此代码自动搜寻类里的foo的属性参数fget=f1,由该参数再自动调用f1(),函数的返回值赋值于ret
print(ret)

p.foo = "zgf" # 此代码自动搜寻类里的foo的属性参数fset=f2,由该参数再自动调用f2(),将字符串zgf传递于f2()的value形参

del p.foo     # 此代码自动搜寻类里的foo的属性参数fdel=f3,由该参数再自动调用f3()

#输出结果
'''
123
zgf
del

'''
View Code 

11.类成员之成员修饰符

# 成员修饰符一
class Foo:
    __cc = 123

    def __init__(self,name,age):
        self.name = name    # 普通字段,默认是共有的,在类的内部和外部都可以访问
        self.__age = age    # 私有字段只有类自身内部可以访问

    def f1(self):
        print(self.name)
        print(self.__age)

    def f2(self):
        print(Foo.__cc)

class Bar(Foo):
    def f3(self):
        print(self.__name)

obj1 = Foo("zgf",23)
print(obj1.name)    # 可以在类的外部访问普通字段
# print(obj1.age)
obj1.f1()       # 在类的内部通过f1()间接访问name字段
print('------------------------------------------------')
obj2 = Bar('zgf',34)
obj2.f1()
obj2.f2()
# obj2.f3() # 不是自身类内部,不可以访问,先调用父类内部的构造方法,本身内部没有,所以不执行

print('------------------------------------------------')
# print(Foo.__cc)   # 外部调用无法访问私有字段
obj3 = Foo('aaa',23)
obj3.f2()

# 输出结果
'''
zgf
zgf
23
------------------------------------------------
zgf
34
123
------------------------------------------------
123
'''
# ------------------------------------------------------------------------
#  成员修饰符二
class Foo:
    def __init__(self,name):
        self.__name = name

    def f1(self):
        print(self.__name)

obj = Foo("zgf")
# print(obj.__name)    在类的外部无法访问私有成员
print(obj._Foo__name)  # 可以访问类内部私有字段,不到万不得已,不要在外部强制访问私有成员

#输出结果:zgf
View Code

12.类成员之特殊成员

上文介绍了Python的类成员以及成员修饰符,从而了解到类中有字段、方法和属性三大类成员,并且成员名前如果有两个下划线,则表示该成员是私有成员,私有成员只能由类内部调用。无论人或事物往往都有不按套路出牌的情况,Python的类成员也是如此,存在着一些具有特殊含义的成员,详情如下:

1. __doc__

  表示类的描述信息

class Foo:
    """ 描述类信息,这是用于看片的神奇 """

    def func(self):
        pass

print(Foo.__doc__)

# 输出结果:描述类信息,这是用于看片的神奇 
View Code 

2. __module__ 和  __class__ 

  __module__ 表示当前操作的对象在那个模块

  __class__     表示当前操作的对象的类是什么

#!/usr/bin/env python
# -*- coding:utf-8 -*-

class C:

    def __init__(self):
        self.name = 'raymond'
lib/aa.py
from lib.aa import C

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

3. __init__

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

class Foo:

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


obj = Foo('raymond') # 自动执行类中的 __init__ 方法
View Code 

4. __del__

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

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

class Foo:

    def __del__(self):
        pass
View Code 

5. __call__

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

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

class Foo:

    def __init__(self):
        pass
    
    def __call__(self, *args, **kwargs):

        print '__call__'


obj = Foo() # 执行 __init__
obj()         # 执行 __call__

Foo()()   #__init__和__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 'wupeiqi'


obj = Foo()
print obj
# 输出:wupeiqi
View Code

8、__getitem__、__setitem__、__delitem__

用于索引操作,如字典。以上分别表示获取、设置、删除数据

#!/usr/bin/env python
# -*- coding:utf-8 -*-
 
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'] = 'raymond'   # 自动触发执行 __setitem__
del obj['k1']           # 自动触发执行 __delitem__

9、__getslice__、__setslice__、__delslice__

 该三个方法用于分片操作,如:列表

#!/usr/bin/env python
# -*- coding:utf-8 -*-
 
class Foo(object):
 
    def __getslice__(self, i, j):
        print '__getslice__',i,j
 
    def __setslice__(self, i, j, sequence):
        print '__setslice__',i,j
 
    def __delslice__(self, i, j):
        print '__delslice__',i,j
 
obj = Foo()
 
obj[-1:1]                   # 自动触发执行 __getslice__
obj[0:1] = [11,22,33,44]    # 自动触发执行 __setslice__
del obj[0:2]                # 自动触发执行 __delslice__

10. __iter__ 

用于迭代器,之所以列表、字典、元组可以进行for循环,是因为类型内部定义了 __iter__ 

class Foo(object):
    pass


obj = Foo()

for i in obj:
    print i
    
# 报错:TypeError: 'Foo' object is not iterable
第一步
#!/usr/bin/env python
# -*- coding:utf-8 -*-

class Foo(object):
    
    def __iter__(self):
        pass

obj = Foo()

for i in obj:
    print i

# 报错:TypeError: iter() returned non-iterator of type 'NoneType'
第二步
#!/usr/bin/env python
# -*- coding:utf-8 -*-

class Foo(object):

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

    def __iter__(self):
        return iter(self.sq)

obj = Foo([11,22,33,44])

for i in obj:
    print i
第三步

以上步骤可以看出,for循环迭代的其实是  iter([11,22,33,44]) ,所以执行流程可以变更为:

#!/usr/bin/env python
# -*- coding:utf-8 -*-
 
obj = iter([11,22,33,44])
 
for i in obj:
    print i
#!/usr/bin/env python
# -*- coding:utf-8 -*-

obj = iter([11,22,33,44])

while True:
    val = obj.next()
    print val
for循环语法内部

11. __new__ 和 __metaclass__

阅读以下代码:

class Foo(object):
 
    def __init__(self):
        pass
 
obj = Foo()   # obj是通过Foo类实例化的对象

上述代码中,obj 是通过 Foo 类实例化的对象,其实,不仅 obj 是一个对象,Foo类本身也是一个对象,因为在Python中一切事物都是对象

如果按照一切事物都是对象的理论:obj对象是通过执行Foo类的构造方法创建,那么Foo类对象应该也是通过执行某个类的 构造方法 创建。

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

所以,obj对象是Foo类的一个实例Foo类对象是 type 类的一个实例,即:Foo类对象 是通过type类的构造方法创建。

那么,创建类就可以有两种方式:

a). 普通方式

class Foo(object):
 
    def func(self):
        print ("hello raymond")

b).特殊方式(type类的构造函数)

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

==》 类 是由 type 类实例化产生

那么问题来了,类默认是由 type 类实例化产生,type类中如何实现的创建类?类又是如何创建对象?

答:类中有一个属性 __metaclass__,其用来表示该类由 谁 来实例化创建,所以,我们可以为 __metaclass__ 设置一个type类的派生类,从而查看 类 创建的过程。

class MyType(type):

    def __init__(self, what, bases=None, dict=None):
        super(MyType, self).__init__(what, bases, dict)

    def __call__(self, *args, **kwargs):
        obj = self.__new__(self, *args, **kwargs)

        self.__init__(obj)

class Foo(object):

    __metaclass__ = MyType

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

    def __new__(cls, *args, **kwargs):
        return object.__new__(cls, *args, **kwargs)

# 第一阶段:解释器从上到下执行代码创建Foo类
# 第二阶段:通过Foo类创建obj对象
obj = Foo()
View Code 

13.其他之isinstance和issubclass 

# isinstance和issubclass
class Foo:
    pass

class Bar(Foo):
    pass

obj1 = Foo()
ret1 = isinstance(obj1,Foo)      # 判断obj1对象是否是Foo类所创建,是:True;   否: False
print(ret1)

obj2 = Bar()
ret2 = isinstance(obj2,Foo)     # 如果Foo是Bar的父类,Bar又创建了obj2对象,则ret2为:Ture
print(ret2)

ret3 = issubclass(Bar,Foo)      # 判断前面的类Bar是否是后面类Foo的子类。是子类:True;  不是子类:False
print(ret3)

# 输出结果
'''
True
True
True
'''
# --------------------------------------------------------------------------------------
# 执行父类的方法
class C1:
    def f1(self):
        print("C1.f1")

class C2(C1):
    def f1(self):
        super(C2,self).f1()     # 主动执行父类C1中的 f1() 方法,建议使用该方法执行,主流形式
        C1.f1(self)     # 另一种形式执行C1类中的f1() 方法,不建议使用该方法执行,非主流形式
        print("C2.f1")

obj = C2()
obj.f1()    # 同时执行C1,C2两个类中的f1() 方法

# 输出结果
'''
C1.f1
C1.f1
C2.f1
'''
View Code 

14.其他之super的应用

15.实例之自定义有序字典

# 自定义有序字典
class MyDict(dict):
    def __init__(self):
        self.li = []
        super(MyDict,self).__init__()

    def __setitem__(self, key, value):
        self.li.append(key)
        super(MyDict,self).__setitem__(key,value)

    def __str__(self):
        temp_list = []
        for key in self.li:
            value = self.get(key)
            temp_list.append("'%s':%s" %(key,value,))
        temp_str = "{" + ",".join(temp_list) + "}"
        return temp_str

obj = MyDict()
obj['k1'] = 123
obj['k2'] = 456
print(obj)

# 输出结果
'''
{'k1':123,'k2':456}
'''
View Code 

16.单例模式

# 单例模式:用来创建单个实例
class Foo:
    instance = None
    def __init__(self,name):
        self.name = name

    @classmethod        # 通过类方法构造出一个单例模式
    def get_instance(cls):  # cls是类名
        if cls.instance:
            return cls.instance
        else:
            obj = cls('raymond')
            cls.instance = obj
            return obj

# 下面创建的两个对象的内存地址都是一样的
obj1 = Foo.get_instance()
print(obj1)
obj2 = Foo.get_instance()
print(obj2)

# 输出结果
'''
<__main__.Foo object at 0x000001E1240B0EF0>
<__main__.Foo object at 0x000001E1240B0EF0>
'''
View Code 

17.基本异常处理

# 基本异常处理
while True:
    num1 = input('num1:')
    num2 = input('num2:')
    try:                    # 在try块里存放的代码,如果出错,下面ex对象捕捉到错误;如果代码正确执行,下面不执行
        num1 = int(num1)
        num2 = int(num2)
        ret = num1 + num2
        print('sum:',ret)
        break

    except IndexError as ex:    # 只捕捉关于索引的错误
        print(ex)

    except ValueError as ex:    # 只捕捉关于数值的错误
        print(ex)

    except Exception as ex:         # 捕捉try中代码块的错误,并把错误描述符封装到ex对象里,这里的Exception会捕捉所有类型的错误
        print("输入数据类型不匹配:",ex) # ex本质是一个str方法
View Code 

18.异常类型

python中的异常种类非常多,每个异常专门用于处理某一项异常!!!

AttributeError 试图访问一个对象没有的树形,比如foo.x,但是foo没有属性x
IOError 输入/输出异常;基本上是无法打开文件
ImportError 无法引入模块或包;基本上是路径问题或名称错误
IndentationError 语法错误(的子类) ;代码没有正确对齐
IndexError 下标索引超出序列边界,比如当x只有三个元素,却试图访问x[5]
KeyError 试图访问字典里不存在的键
KeyboardInterrupt Ctrl+C被按下
NameError 使用一个还未被赋予对象的变量
SyntaxError Python代码非法,代码不能编译(个人认为这是语法错误,写错了)
TypeError 传入对象类型与要求的不符合
UnboundLocalError 试图访问一个还未被设置的局部变量,基本上是由于另有一个同名的全局变量,
导致你以为正在访问它
ValueError 传入一个调用者不期望的值,即使值的类型是正确的
常用异常 
ArithmeticError
AssertionError
AttributeError
BaseException
BufferError
BytesWarning
DeprecationWarning
EnvironmentError
EOFError
Exception
FloatingPointError
FutureWarning
GeneratorExit
ImportError
ImportWarning
IndentationError
IndexError
IOError
KeyboardInterrupt
KeyError
LookupError
MemoryError
NameError
NotImplementedError
OSError
OverflowError
PendingDeprecationWarning
ReferenceError
RuntimeError
RuntimeWarning
StandardError
StopIteration
SyntaxError
SyntaxWarning
SystemError
SystemExit
TabError
TypeError
UnboundLocalError
UnicodeDecodeError
UnicodeEncodeError
UnicodeError
UnicodeTranslateError
UnicodeWarning
UserWarning
ValueError
Warning
ZeroDivisionError
更多异常
dic = ["zgf", 'raymond']
try:
    dic[10]
except IndexError as ex:
    print (ex)
实例:IndexError
dic = {'k1':'v1'}
try:
    dic['k20']
except KeyError as ex:
    print (ex)
实例:KeyError
s1 = 'hello'
try:
    int(s1)
except ValueError as ex:
    print (ex)
实例:ValueError

19.主动触发异常

# 主动触发异常
# 下面是异常处理的完整代码块
try :

    # raise Exception("主动触发一个错误") # 主动触发异常错误提示信息

    s = "raymond"       # 这部分属于代码发生异常错误
    int(s)
    print(123)

except ValueError as ex:    # 如果try里的代码块报错,则先搜索此处的具体异常错误类型并匹配,匹配则输出下面的print错误内容
    print(ex)
except Exception as ex:     # 如果上面的错误类型不匹配,则此处捕捉所有类型错误,输出下面的print内容
    print(ex)               # 此处输出上面主动触发的异常错误信息

else:       # 如果try里的代码块没有报错,则执行此处的else代码块
    pass
finally:    # 此处不管try里的代码块有没有错误,都执行finally下的代码块
    pass
View Code 

20.自定义异常和断言

# 自定义异常
class zgfException(Exception):

    def __init__(self, msg):
        self.message = msg

    def __str__(self):
        return self.message
try:
    raise zgfException('我的异常')
except zgfException as ex:
    print(ex)

# 输出结果:我的异常
# -----------------------------------------------------------------------------------------
# 断言

print("断言前执行")

# 断言的用法
assert 1==1     # 条件成立,则继续执行下面代码
assert 1==2     # 条件不成立,则触发异常错误,下面代码无法再执行

print("断言后执行")
View Code

 

posted @ 2018-02-01 11:16  zgfraymond  阅读(293)  评论(0编辑  收藏  举报