2、面向对象

声明

python2需要在类名后括号内写明父类,python3如果不写后面的括号,默认继承object

#class 类名(父类,没有就是object):
class Student(object):
    pass

实例化

class Student(object):
    pass

#实例变量名 = 类名()
stu = Student()
print(stu)

>>>>
#打印可以看到,实例属于S
<__main__.Student object at 0x000001538F0097F0>

属性

由于类可以起到模板的作用,因此,可以在创建实例的时候,把一些我们认为必须绑定的属性强制填写进去。通过定义一个特殊的__init__方法(构造方法),在创建实例的时候,就把属性绑上去

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

class Student(object):
    #构造方法
    def __init__(self,name,age,sex):
        self.name = name
        self.age = age
        self.sex = sex

stu = Student('lucy',18,'女')

print(stu.name)
print(stu.age)
print(stu.sex)

私有属性

要让内部属性不被外部访问,可以把属性的名称前加上两个下划线__,在Python中,实例的变量名如果以__开头,就变成了一个私有变量(private),只有内部可以访问,外部不能访问

不能直接访问__name是因为Python解释器对外把__name变量改成了_Student__name,所以,仍然可以通过stu._Student__name来访问__name变量,但是不要这么做

class Student(object):
    #构造方法
    def __init__(self,name,age,sex):
        self.__name = name
        self.__age = age
        self.__sex = sex

stu = Student('lucy',18,'女')

方法

要定义一个方法,除了第一个参数必须是self外,其他和普通函数一样

class Student(object):
    #默认值私有属性
    __sex = '男'
    #构造方法
    def __init__(self,name,age,sex):
        self.__name = name
        self.__age = age
        self.__sex = sex

    def showMsg(self):
        "展示学生信息"
        print("姓名:",self.__name,"年龄:",self.__age,"性别:",self.__sex)

stu = Student('lucy',18,'女')
stu.showMsg()

私有方法

私有方法和私有属性一样,在方法名前加两个下划线__,方法就变成了私有的

私有方法同样可以通过stu._Student__sayHello()进行访问,但是不要这么做

class Student(object):
    #构造方法
    def __init__(self,name,age,sex):
        self.__name = name
        self.__age = age
        self.__sex = sex

    def showMsg(self):
        "展示学生信息"
        print("姓名:",self.__name,"年龄:",self.__age,"性别:",self.__sex)
    
    def sayHello(self):
        "私有方法"
        print('Hello')

stu = Student('lucy',18,'女')
stu.showMsg()

静态

静态属性

在类中直接定义的属性,就是静态属性,这个属性属于类,被该类所有实例共有

class Person(object):
    name = 'lucy'

print(Person.name)

p = Person()
print(p.name)

'''
lucy
lucy
'''

静态方法

python中,可以使用装饰器@classmethod@staticmethod

如果在@staticmethod中要调用到这个类的一些属性方法,只能直接类名.属性名或类名.方法名。

而@classmethod因为持有cls参数,可以来调用类的属性,类的方法,实例化对象等,避免硬编码。

class Person(object):

    @staticmethod
    def sayHello():
        print('Hello World')

Person.sayHello()

p = Person()
p.sayHello()

'''
Hello World
Hello World
'''

继承和多态

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

可以使用isinstance(obj,class)来查看对象obj是否属于类class

class Animal(object):
	pass
#狗、猫类继承于动物类
class Dog(Animal):
    pass
class Cat(Animal):
    pass
#实例化
d = Dog()
c = Cat()

print(isinstance(d,Animal))
print(isinstance(c,Animal))

属性的继承

子类会继承父类中已经声明的属性,并且可以使用默认值

class Animal(object):
    name = '动物'

#狗类继承于动物类
class Dog(Animal):
    pass

#实例化
d = Dog()

#调用父类声明的属性
print(d.name)

方法的继承

子类会继承父类的所有方法,包括构造方法__init__()

class Animal(object):
    def __init__(self,name):
        self.__name = name
    def run(self):
        "动物都有的行为"
        print('%s在跑' % self.__name)

#狗、猫类继承于动物类
class Dog(Animal):
    pass
class Cat(Animal):
    pass
#实例化,继承父类的构造方法
d = Dog("狗")
c = Cat("猫")
#继承父类的方法
d.run()
c.run()

方法重写

如果你的父类方法的功能不能满足你的需求,你可以在子类重写你父类的方法

class Animal(object):
    def run(self):
        print("动物在跑")

class Dog(Animal):
    #重写父类方法run()
    def run(self):
        print("狗子在跑")

dog = Dog()
dog.run()

super()函数

super() 函数是用于调用父类(超类)的一个方法,也就是使用子类对象,调用父类已被重写的方法

super() 是用来解决多重继承问题的,直接用类名调用父类方法在使用单继承的时候没问题,但是如果使用多继承,会涉及到查找顺序(MRO)、重复调用(钻石继承)等种种问题。

MRO就是类的方法解析顺序表, 其实也就是继承父类方法时的顺序表。

class Animal(object):
    def run(self):
        print("动物在跑")

class Dog(Animal):
    #重写父类方法run()
    def run(self):
        print("狗子在跑")

dog = Dog()
dog.run()
#使用子类对象调用父类已经被重写的方法
super(Dog,dog).run()

多继承

python支持多继承,即可以继承于多个父类

需要注意圆括号中父类的顺序,若是父类中有相同的方法名,而在子类使用时未指定,python从左至右搜索即方法在子类中未找到时,从左到右查找父类中是否包含方法

class Animal(object):
    def run(self):
        "动物都有的行为"
        print('动物可以跑')

class Pet(object):
    def play(self):
        "宠物都有的行为"
        print('宠物可以和主人玩')

class Cat(Animal,Pet):
    pass

cat = Cat()

cat.play()
cat.run()

多态

python不支持多态也用不到多态,多态的概念是应用于java和C#这一类强类型语言中,而Python崇尚鸭子类型(Duck Typing)

鸭子类型:是一种动态类型的风格。一个对象有效的语义,不是由继承自特定的类或实现特定的接口,而是由当前方法和属性的集合决定。这个概念的名字来源于由James Whitcomb Riley提出的鸭子测试,“鸭子测试”可以这样表述:“当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。”

也就是说,我们可以编写一个函数,它接受一个类型为鸭的对象,并调用它的走和叫方法。在使用鸭子类型的语言中,这样的一个函数可以接受一个任意类型的对象,并调用它的走和叫方法。如果这些需要被调用的方法不存在,那么将引发一个运行时错误

class Duck(object):
    def go(self):
        print('鸭子走')
    def run(self):
        print('鸭子跑')
class Dog(object):
    def go(self):
        print('狗走')
    def run(self):
        print('狗跑')
#python的多态,不在于是哪个类,而是只要该对象有这两个方法就可以
def handler(duck):
    duck.go()
    duck.run()

duck = Duck()
dog = Dog()

handler(duck)
handler(dog)

获取对象信息

type()可以获取对象的类型,返回一个Class类型的变量,可以使用==判断两个对象是否同一类型

class Person(object):
    def __init__(self,name,age):
        self.__name = name
        self.__age = age
    def showMsg(self):
        print('姓名',self.__name,'年龄',self.__age)

person = Person('lucy',18)

print(type(person)) # <class '__main__.Person'>

isinstance(obj,class)来查看对象obj是否属于类class

class Person(object):
    def __init__(self,name,age):
        self.__name = name
        self.__age = age
    def showMsg(self):
        print('姓名',self.__name,'年龄',self.__age)

person = Person('lucy',18)

print(isinstance(person,Person)) # Ture

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

class Person(object):
    def __init__(self,name,age):
        self.__name = name
        self.__age = age
    def showMsg(self):
        print('姓名',self.__name,'年龄',self.__age)

person = Person('lucy',18)

print(dir(person))

'''
['_Person__age', '_Person__name', '__class__', '__delattr__',
 '__dict__', '__dir__', '__doc__', '__eq__', '__format__',
  '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
   '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__',
    '__new__', '__reduce__', '__reduce_ex__', '__repr__',
     '__setattr__', '__sizeof__', '__str__', '__subclasshook__',
      '__weakref__', 'showMsg']
'''

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

获取对象某个属性的值

class Person(object):
    def __init__(self,name,age):
        self.__name = name
        self.__age = age
    def showMsg(self):
        print('姓名',self.__name,'年龄',self.__age)

person = Person('lucy',18)
#获取person对象_Person__name属性的值
print(getattr(person,'_Person__name'))
#获取person对象_Person__name属性的值,如果不存在,返回404
print(getattr(person,'_Person__name',404))

设置对象某个属性的值

class Person(object):
    def __init__(self,name,age):
        self.__name = name
        self.__age = age
    def showMsg(self):
        print('姓名',self.__name,'年龄',self.__age)

person = Person('lucy',18)

#设置person对象,_Person__name属性值为tom
setattr(person,'_Person__name','tom')

person.showMsg()

判断对象是否含有某个属性

class Person(object):
    def __init__(self,name,age):
        self.__name = name
        self.__age = age
    def showMsg(self):
        print('姓名',self.__name,'年龄',self.__age)

person = Person('lucy',18)

#判断对象person中是否含有属性_Person__name
print(hasattr(person,'_Person__name'))

此外,这三个函数也可以作用于对象的方法

class Person(object):
    def __init__(self,name,age):
        self.__name = name
        self.__age = age
    def showMsg(self):
        print('姓名',self.__name,'年龄',self.__age)

person = Person('lucy',18)

#判断对象person中是否含有方法showMsg
print(hasattr(person,'showMsg'))

#获取对象person的方法showMsg
show = getattr(person,'showMsg')

#调用方法
show() # 姓名 lucy 年龄 18