面向对象编程

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

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

面向对象三大特点:封装,集成,多态。

类和实例


 

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

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

通过在实例上调用方法,我们就直接操作了对象内部的数据,但无需知道方法内部的实现细节。

和静态语言不同,Python允许对实例变量绑定任何数据,也就是说,对于两个实例变量,虽然它们都是同一个类的不同实例,但拥有的变量名称都可能不同:

class Student(object):

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

    def printSource(self):
        print("%s的成绩是:%s"%(self.name,self.source))



#创建出一个学生对象,这个对象拥有name和score这两个属性(Property)
lisa=Student('Lisa',98)
sam=Student('Sam',70)

#调用对象的printSource方法
lisa.printSource()
sam.printSource()
lisa.age=18
print(lisa.age)
print(sam.age)

输出:

Lisa的成绩是:98
Sam的成绩是:70
18
Traceback (most recent call last):
File "/home/wangxy/PycharmProjects/module/Student.py", line 22, in <module>
print(sam.age)
AttributeError: 'Student' object has no attribute 'age'


 

访问限制


 

1.如果要让内部属性不被外部访问,可以把属性的名称前加上两个下划线__,即成为私有变量(private),只有内部可以访问,外部不能访问。

2.在Python中,变量名类似__xxx__的,也就是以双下划线开头,并且以双下划线结尾的,是特殊变量,特殊变量是可以直接访问的,不是private变量,所以,不能用__name____score__这样的变量名

3.以一个下划线开头的实例变量名,比如_name,这样的实例变量外部是可以访问的,但是,按照约定俗成的规定,当你看到这样的变量时,意思就是,“虽然我可以被访问,但是,请把我视为私有变量,不要随意访问”

4.设置为私有变量后,如果要获取属性可以设置方法get_name,修改属性可以使用set_name ,和java类似

class Student(object):

    def __init__(self,name,source):
        self.__name=name
        self.__source=source

    def print_source(self):
        print("%s的成绩为:%s"%(self.__name,self.__source))

    def get_name(self):
        return self.__name

    def get_source(self):
        return self.__source

    def set_name(self,name):
        self.__name=name
    #在方法中,可以对参数做检查,避免传入无效的参数.而直接赋值无法做到
    def set_source(self,source):
        if 0<=source<=100:
            self.__source=source
        else:
            print("无效的数据")

Tina=Student('Tina',59)
Tina.set_source(80)
Tina.print_source();
Tina.__name

输出:
Tina的成绩为:80
Traceback (most recent call last):
  File "/home/wangxy/PycharmProjects/module/StuPrivate.py", line 29, in <module>
    Tina.__name
AttributeError: 'Student' object has no attribute '__name'

 

继承和多态


 

在OOP程序设计中,当我们定义一个class的时候,可以从某个现有的class继承,新的class称为子类(Subclass),而被继承的class称为基类、父类或超类(Base class、Super class)。

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

在继承关系中,如果一个实例的数据类型是某个子类,那它的数据类型也可以被看做是父类。但是,反过来就不行:

class Animal(object):
    def run(self):
        print('Animal is running……')
from TestAnimal.Animal import Animal

#继承了Animal类
class Dog(Animal):
    #继承后对父类的方法进行重写,这叫多态
    def run(self):
        print('Dog is running')

dog=Dog()
#继承后自动拥有父类方法
dog.run()

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

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

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

 

静态语言  动态语言


 

对于静态语言(例如Java)来说,如果需要传入Animal类型,则传入的对象必须是Animal类型或者它的子类,否则,将无法调用run()方法。

对于Python这样的动态语言来说,则不一定需要传入Animal类型。我们只需要保证传入的对象有一个run()方法就可以了:

这就是动态语言的“鸭子类型”,它并不要求严格的继承体系,一个对象只要“看起来像鸭子,走起路来像鸭子”,那它就可以被看做是鸭子。

Python的“file-like object“就是一种鸭子类型。对真正的文件对象,它有一个read()方法,返回其内容。但是,许多对象,只要有read()方法,都被视为“file-like object“。许多函数接收的参数就是“file-like object“,你不一定要传入真正的文件对象,完全可以传入任何实现了read()方法的对象。

 

 

获取对象信息


 一、判断对象类型使用type()方法:

#判断对象类型,使用type(),返回的是对象的类
print(type(a))
print(type(123))

#判断两个变量的type类型是否相同
print(type(a)==type(d))
print(type(123)==int)

#判断一个变量是否是函数,使用type模块中定义的常量
print(type(Animal.run)==types.FunctionType)
print(type(abs)==types.BuiltinFunctionType)

 

二、判断class的类型,使用isinstance()

#判断一个变量是否是某个类型
print('a is animal?',isinstance(a,Animal))
print('a is dog?',isinstance(a,Dog))

#下面判断得出d不仅是dog,还是animl类
print('d is animal?',isinstance(d,Animal))
print('d is dog?',isinstance(d,Dog))

 

三、能用type判断的也能用isinstance()判断

#使用isinstance()判断类型
print('\'a\' is str?',isinstance('a',str))
#判断一个变量是否是某些类型的一种
print('[1,2,3] is list or tuple?',isinstance([1,2,3],(list,tuple)))

 

四、使用dir(object)

如果要获取一个对象的所有属性和方法,可以使用dir(object)

#获取一个对象的所有属性和方法
print(dir(d))

结果:
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'run']

 

仅仅把属性和方法列出来是不够的,配合getattr()setattr()以及hasattr(),我们可以直接操作一个对象的状态:

#操作一个对象的状态:hasattr(),getattr(),setattr()
class myObject(object):
    def __init__(self):
        self.x=9
    def power(self):
        return self.x * self.x

test=myObject();
#test对象有没有x属性
print(hasattr(test,'x'))
#设置test对象的y=20
setattr(test,'y',20)
print(hasattr(test,'y'))
#获取test的x属性值
print(getattr(test,'x'))

#获取不存在的属性
getattr(test,'z')

#获取不存在的属性,可以设置默认值,当属性不存在时,输出默认值
print(getattr(test,'z',404))


输出:
True
True
9
Traceback (most recent call last):
  File "/home/wangxy/PycharmProjects/module/TestAnimal/Animal.py", line 75, in <module>
    getattr(test,'z')
AttributeError: 'myObject' object has no attribute 'z'

404
#也可以获取属性的方法:
print(getattr(a,'run'))

输出
<bound method Animal.run of <__main__.Animal object at 0x7fee8db2ea20>>

通过内置的一系列函数,我们可以对任意一个Python对象进行剖析,拿到其内部的数据。要注意的是,只有在不知道对象信息的时候,我们才会去获取对象信息

一个正确的用法的例子如下:

def readImage(fp):
    if hasattr(fp, 'read'):
        return readData(fp)
    return None

假设我们希望从文件流fp中读取图像,我们首先要判断该fp对象是否存在read方法,如果存在,则该对象是一个流,如果不存在,则无法读取。hasattr()就派上了用场。

请注意,在Python这类动态语言中,根据鸭子类型,有read()方法,不代表该fp对象就是一个文件流,它也可能是网络流,也可能是内存中的一个字节流,但只要read()方法返回的是有效的图像数据,就不影响读取图像的功能。

 

实例属性和类属性


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

给实例绑定属性的方法是通过实例变量,或者通过self变量:

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

s1=Student('wxy')
s1.source=90
print(s1.name,s1.source)

 

类属性:归类所有,所有实例都可以访问到。

class Stu(object):
    #类属性
    name='Student'


s=Stu()      #创建实例
print(s.name)#打印实例name属性,因为不存在,所以会继续查找类的name属性
s.name='wxy' #向实例赋值
print(s.name)#打印实例name属性,实例属性优先级高于类属性,因此,打印实例属性
print(Stu.name)#类属性并未消失,使用stu.name可以访问
del s.name   #删除实例属性
print(s.name)#实例属性已被删除,则会显示类属性

 

 


posted @ 2017-10-26 17:20  wangxy92  阅读(170)  评论(0编辑  收藏  举报