您的位置:首页 - 教程 - python - 正文
python基础篇【第八篇】面向对象(下)

一、 面向对象特性之多态:

  上一篇已经介绍了面向对象的三大特性的前两种(封装、继承),下面来说说第三种多态,在python中用不上,但在其他语言中很重要。

多态的意思就是多种类型、多种形态,比如字符类型,数字,字典,列表等。在python中定义类或函数时不需要,指定是那种数据类型全都支持,但是在java、c#等其他语言中需要指定如下实例:

def func(A arg)
               print(arg)
          arg :参数必须是A 类型,或A类型的子类

 

二、类成员:

  类成员分为三大类:字段、方法、属性

 

1、字段

在类中字段分为静态字段和普通字段

普通字段(动态字段):存储在对象中,由对象调用

静态字段:存储在类中,由类调用,在代码加载时,就已经创建了

调用规则:

1、一般情况下自己访问自己;

2、普通字段,只能由对象访问

3、静态字段用类访问,(万不得已的时候也可以用对象访问,不建议使用)

 

两者区别如下:

class Foo:
    CC = 123  # CC是静态字段,保存在类中

    def __init__(self):
        self.name = "tom"  # name 就是普通字段,保存在对象中

 

实际调用如下:

class Province:
    contry="中国" #静态字段

    def __init__(self,name):
        self.name=name   #普通字段

sx=Province("河南")
#静态调用 如 Province.contry
#普通调用 如 sx.name
print(Province.contry,sx.name)

#显示结果
中国 河南

 

静态字段存储在类中, 只在内存中保存一份;

普通字段存储在每个对象中,需要在每个对象中都保存一份

应用场景:如果创建对象时,都要需要某一个相同的字段,可以把字段设置为静态字段,节省内存

 

2、方法

方法都属于类包括:

静态方法:属于类,由类来调用执行,无默认参数,等同于函数。创建方式:  方法上边加个@staticmethod

普通方法:由对象调用,至少需要一个self参数。执行普通方法时,self就是对象本身,自动将调用该方法的对象赋值给self

类方法:是静态方法的一种特殊形式。由类调用,至少要有一个参数cls,值为类名。创建方式: @classmethod

class Province:
    contry="中国"

    def __init__(self,name):   #普通方法
        self.name=name

    def show(self):
        print("普通方法")

    @classmethod
    def class_show(cls):     #类方法
        print(cls)

    @staticmethod
    def static_show():   #静态方法
        print("静态方法")

sx=Province("河南")

#普通方法调用
sx.show()

#类方法调用
Province.class_show()

#静态方法调用
Province.static_show()



#显示结果
普通方法
<class '__main__.Province'>    #是这个类名
静态方法

相同点:由于所有的方法都属于类, 所以在内存中存储只保存一份。

不同点:由于各种方法的调用方式不同,调用方法时自动传入的参数不同。

给上面字段一样,在对象中也是可以调用静态方法和类方法的。不到万不得已还是不要用,要遵循变成原则。

 

3、属性

属性就是普通方法的变种

下面来看一下属性的定义:

class Foo:
    
    def show(self):
        print("普通方法")
      
    @property   #定义属性
    def prop(self):
        pass

#调用
obj=Foo()
obj.show()   #调用方法
obj.prop    #调用属性

注意:

定义时,在普通方法的基础上添加@property装饰器;仅有一个参数self

调用时,无需加括号

 

还有一种属性的定义调用方法:

class Foo():

    @property
    def price(self):
        print('查询')

    @price.setter
    def price(self, value):
        print('设置')

    @price.deleter
    def price(self):
        print('删除')

# ############### 调用 ###############
obj = Foo()

obj.price          # 自动执行 @property 修饰的 price 方法,并获取方法的返回值

obj.price = 123    # 自动执行 @price.setter 修饰的 price 方法,并将  123 赋值给方法的参数

del obj.price      # 自动执行 @price.deleter 修饰的 price 方法

#执行结果
查询
设置
删除

 

三、类成员修饰符

公有成员:就是在哪都能访问,

私有成员,只有在类的内部才能放问,其他都不能访问,继承关系也不能

定义私有成员:命名是前面是两个下划线,(特殊成员除外,例如:__init__、__call__、__dict__等)

如下:

class Foo:
    def __init__(self):
        self.name="公有字段"
        self.__fuck="私有字段"

 

四、类的特殊成员

1、__init__

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

class Foo:

    def __init__(self,name):
        self.name = name # name 就是普通字段
        self.job="IT"

obj=Foo("tom")   #自动执行类中的__init__方法
print(obj.name)
print(obj.job)

#结果
tom
IT

2、__doc__

  表示类的描述信息,就是注释

class Foo:
    '''
    描述信息
    '''
    def __init__(self,name):
        self.name = name # name 就是普通字段
        self.job="IT"
print(Foo.__doc__)

#显示结果
   描述信息
    
View Code

3、__del__

  析构方法,用于对象在内存中垃圾回收时,自动触发执行

4、__call__

  在对象后面加括号,执行

对于 __call__ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()

class Foo:

    def __init__(self,name):
        self.name = name # name 就是普通字段

    def __call__(self, *args, **kwargs):
        print("执行call方法")
obj=Foo("test")  #执行__init__方法
obj()   #执行__call__方法
View Code

5、__module__、__class__

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

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

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

class Foo:
    '''
    这是类的描述信息, 由特殊成员 __doc__调用
    '''
    def __init__(self,name):
        self.name = name
        # print(self.name)

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

bin/modules.py内容
bin/modules.py
from bin import modules

obj = modules.Foo('DBQ')
print(obj.__module__)   # 查看当前操作的对象属于哪个模块

print(obj.__class__)       #查看当前操作的对象的类是哪个


#执行结果:
bin.modules
<class 'bin.modules.F1'>
View Code

6、__dict__

  获取对象或类中的所有成员

class Foo:
    CC="test"
    def __init__(self,name):
        self.name = name # name 就是普通字段


#获取类中的所有成员
print(Foo.__dict__)

#获取对象中的成员
obj=Foo("tom")
print(obj.__dict__)

#显示结果
{'__weakref__': <attribute '__weakref__' of 'Foo' objects>, 'CC': 'test', '__doc__': None, '__module__': '__main__', '__dict__': <attribute '__dict__' of 'Foo' objects>, '__init__': <function Foo.__init__ at 0x00000041DAED61E0>}
{'name': 'tom'}
View Code

7、__str__

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

class Foo:
    CC="test"
    def __init__(self,name):
        self.name = name # name 就是普通字段

    def __str__(self):
        return "hi tom"

obj=Foo("tom")
print(obj)

#结果
hi tom
View Code

8、__iter__

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

class Foo():

    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)
View Code

9、__getitem__、__setitem__、__deltiem__

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

class Foo():
    def __getitem__(self, item):
        print("执行get操作,调用__getitem__方法")
    def __setitem__(self, key, value):
        print("执行set操作,调用__setitem__方法")
    def __delitem__(self, key):
        print("执行del操作,调用__delitem__方法")

obj=Foo()
obj['a1']   #get操作,自动触发__getitem__方法
obj["a1"]=[1,2,3,4,5] #set操作,自动调用__setitem__方法"
del obj['a1']   #del操作,自动调用__delitem__方法

#结果
执行get操作,调用__getitem__方法
执行set操作,调用__setitem__方法
执行del操作,调用__delitem__方法
View Code

10、__getslice__、__setslice__、__delslice__

  用于分片操作

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__
python2.7执行

在python3中还是运用的__getitem__、__setitem__、__deltiem__

class Foo():

    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)

    def __getitem__(self, item):
        print("执行get操作,调用__getitem__方法")
    def __setitem__(self, key, value):
        print("执行set操作,调用__setitem__方法")
    def __delitem__(self, key):
        print("执行del操作,调用__delitem__方法")
obj = Foo()

obj[-1:1]                   # 自动触发执行 __getslice__
obj[0:1] = [11,22,33,44]    # 自动触发执行 __setslice__
del obj[0:2]                # 自动触发执行 __delslice__

#结果
执行get操作,调用__getitem__方法
执行set操作,调用__setitem__方法
执行del操作,调用__delitem__方法
python3中

11. __new__ 、 __metaclass__

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

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

obj = F1('tom')

print(type(obj))

print(type(F1))

# #执行代码结果:
# <class '__main__.F1'>      #表示 obj对象由 F1类实例化而来
# <class 'type'>                  #表示 F1类由 type 类创建

python中一切介对象,上述代码中obj是F1的一个对象,那么可以推理F1其实也是一个对象

所以,obj对象时F1类的一个实例,F1类对象时type类的一个实例,F1类对象时通过type类构造方法创建

创建方法有两种普通方法、特殊方法:

普通方法:

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

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

特殊方法:

def show_info(self):
    print('tom')

F1 = type('F1',(object,),{'show_info':show_info})
#第一个参数: 类名
#第二个参数: 当前类的基类
#第三个参数: 类成员

obj = F1()
obj.show_info()

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

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

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

 

 五、面向对象其他相关知识

  1、isinstance(obj,cls)

 检查是否obj是否是类 cls 的对象

判断一个对象是不是类创建的()实例,返回布尔值,继承的父类也为真

class F1:
    pass

class F2(F1):
    pass

obj = F2()

print(isinstance(obj,F1))    #查看是否是父类的实例, 为真
print(isinstance(obj,F2))    #查看是否是F2类的实例, 为真

 

2、issubclass(F1,F2)

检查F1是否是F2的子类

查看是否是某类的子类

class F1:
    pass

class F2(F1):
    pass

obj = F2()

print(issubclass(F2,F1))   #查看F2是否是F1的子类, 为真
print(issubclass(F1,F2))   #查看F1是否是F2的子类, 为假

 

3、 super 

扩展别人的源码 ,尽量不在源码中修改

class C1:
    def f1(self):
        print('C1.f1')

class C2(C1):
    def f1(self):
        super(C2,self).f1()   #在执行C2代码之前,执行C1中的f1方法 也就是C2父类的f1方法
        print('C2.f1')

obj = C2()
obj.f1()

#结果
C1.f1
C2.f1

 

五、异常处理与捕获

1、异常处理基础

增加友好性,在程序出现bug中一般不会将错误信息显示给用户,而是显示一个页面

while True:

    num = input('请输入你一个或多个整数: ').strip()

    try:
        num = int(num)
        print('你输入的数字是: %d'%num)
    except Exception:
        print('%s, 你输入的不是一个整数格式!'%Exception)


# 如果输入的是一个整数类型,将返回输入的号码
# 如果输入的是其他的类型,如字符串、浮点数等,会提示用户输入的不是一个整数格式!

####执行结果:
请输入你一个或多个整数: 123
你输入的数字是: 123
请输入你一个或多个整数: a
<class 'Exception'>, 你输入的不是一个整数格式!
请输入你一个或多个整数: 1.
<class 'Exception'>, 你输入

 

2.异常处理

捕获异常可以使用 try / except语句。try: 用来检测语句块中的错误,从而让 except中语句捕获的异常信息并处理。

打开一个文件,往文件中写入内容,并且没有发生异常:

try:
    f = open('test.txt','w')
    f.write('测试文件,用于测试异常捕获')
except IOError:
    print('Error: 写入失败, 没有找到文件或者权限不足!')
else:
    print('写入成功!')
    f.close()

#执行结果:
写入成功!

#文件内容:
Daniel-Mac:blog daniel$ cat test.txt &&echo
测试文件,用于测试异常捕获

修改文件的权限没有写,而后在打开文件,往文件中写入内容,查看异常:

chmod -w test.txt 
try:
    f = open('test.txt','w')
    f.write('测试文件,用于测试异常捕获')
except IOError:
    print('Error: 写入失败, 没有找到文件或者权限不足!')
else:
    print('写入成功!')
    f.close()

#再次执行代码:
Error: 写入失败, 没有找到文件或者权限不足!

 常用异常:

AttributeError 试图访问一个对象没有的树形,比如foo.x,但是foo没有属性x
IOError 输入/输出异常;基本上是无法打开文件
ImportError 无法引入模块或包;基本上是路径问题或名称错误
IndentationError 语法错误(的子类) ;代码没有正确对齐
IndexError 下标索引超出序列边界,比如当x只有三个元素,却试图访问x[5]
KeyError 试图访问字典里不存在的键
KeyboardInterrupt Ctrl+C被按下
NameError 使用一个还未被赋予对象的变量
SyntaxError Python代码非法,代码不能编译(个人认为这是语法错误,写错了)
TypeError 传入对象类型与要求的不符合
UnboundLocalError 试图访问一个还未被设置的局部变量,基本上是由于另有一个同名的全局变量,
导致你以为正在访问它
ValueError 传入一个调用者不期望的值,即使值的类型是正确的


实例:

下标异常

dic = ["tom", 'jerry']
try:
    dic[10]
except IndexError, e:    
    print e


key异常

dic = {'k1':'v1'}
try:
    dic['k20']
except KeyError, e:
    print e

 

元素异常

s1 = 'hello'
try:
    int(s1)
except ValueError, e:
    print e

 

对于上述实例,异常类只能用来处理指定的异常情况,如果没有指定异常则无法处理。

# 未捕获到异常,程序直接报错
 
tt = 234
try:
    str(tt)
except IndexError,e:
    print e

 

如果想通吃各种异常,python中也提供了一个万能异常Exception,就能捕获任意异常,目的是保证程序能正常运行

tt = 234
try:
    str(tt)
except Exception,e:
    print e

 

如果你还想要知道异常是什么,在那一块报错了,还有一个更周全的方案,如下:

s1 = 'hello world'
try:
    int(s1)
except KeyError:
    print('Error: Key错误!')
except IndexError:
    print('Error: 索引错误!')
except ValueError:
    print('Error: 值错误!')
except Exception:
    print('Error: 出错了!')
else:
    print('你的值是: %s'%s1)

 

六、设计模式,单实例

例模式,顾名思义,也就是单个实例的意思。

模式特点:保证类仅有一个实例,并提供一个访问它的全局访问点。

class Singleton:
    __instance = None   #定义一个私有静态字段为初始值

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

    def show(self):
        print(self.name)
        return 'test_instance'

    @classmethod
    def get_instance(cls):
        if cls.__instance:  #如果字段内有值,直接返回字段值
            return cls.__instance
        else:                     
            obj = cls('DBQ')          #实例化
            cls.__instance = obj    #将对象赋值给字段
            return cls.__instance   #返回对象


a = Singleton.get_instance()
b = Singleton.get_instance()

print(a)
print(id(a))   #内存地址和b相同
print()
print(b)
print(id(b))    #内存地址和a相同
# 后面再来几个对象,也是一样的!

#代码执行结果:
<__main__.Singleton object at 0x101b769b0>

<__main__.Singleton object at 0x101b769b0>
单实例

单例模式的存在主要是保证当前内存中存在单个实例,避免内存资源浪费。


评论: