面向对象

1.概述(理解即可)

# 面向对象编程(Object Oriented Programming,OOP),是一种程序设计思想,解决软件复用的设计和编程方法。
# 本质是以建立模型体现出来的抽象思维过程。模型用于反映现实世界中事物特征。
# 任何一种事物都可以作为对象,“万物皆对象” ,对象包含了数据和操作数据的函数。
# 例如,在实际应用中,可以将一个人抽象为一个类,该类中包含一个人的“姓名,年龄,性别”等信息。
# Python是一种比较典型的面向对象的语言,Python中的数据都是对象。

面向对象:把问题进行抽象,对函数进行分类和封装,把相关的数据和方法作为一个整体来看待。
降低代码之间的耦合度,强调模块开发的思想。
自由拆分和组合功能,以不同的组合形式体现,从而提供不同的服务。

# 类: 用来描述具有相同的属性和方法的对象的集合。定义了该集合中每个对象所共有的属性和方法。对象是类的实例。
# 对象:通过类定义的数据结构实例。
# 实例化:创建一个类的实例,类的具体对象。
# 类的成员:包括属性,方法等。
# 继承:即一个类(derived class)继承基类(base class)的属性和方法。
# 方法重写:如果从父类继承的方法不能满足子类的需求,可以对其进行改写,这个过程叫方法的覆盖(override),也称为方法的重写。

2.类的创建与使用

python面向对象详解 python 面向对象知乎_父类

构造方法

python面向对象详解 python 面向对象知乎_python面向对象详解_02

变量和方法名前面的下划线

python面向对象详解 python 面向对象知乎_静态方法_03


python面向对象详解 python 面向对象知乎_父类_04

3.类的成员

属性和方法统称为类的成员

1.属性

对象属性
对象属性又称为实例属性,通常在__init__中进行初始化
# 创建类
class Person:
    # 构造方法
    def __init__(self, name, age):
        self.name = name
        self.age = age

p = Person('小吴', 16)
print(p.age)#16
p.age+=1
print(p.age)#17
类属性
类属性:类属性是类本身的属性,无论根据该类创建了多少的对象,
类属性依然只有一个,所以对象与对象之间可以共享类属性。
类似于其他某些高级语言中的静态公共变量。
***区别在于Python中,对象也可以调用类属性。***
# 创建类
class Person:
    info = 'this is a person.'
    # 构造方法
    def __init__(self, name, age):
        self.name = name
        self.age = age  
p = Person('小吴', 16)
print(p.info)

除了在类的内部定义属性之外,在类的外部也能定义属性

# 创建类
class Person:
    info = 'this is a person.'
    # 构造方法
    def __init__(self, name, age):
        self.name = name
        self.age = age  

Person.gender = 'unknown'
print(Person.gender) #unkonwn

如果属性是私有的,那么无法直接使用标识符.属性 进行修改或者查询,但是可以通过函数间接对属性进行修改。

class Person:
    def __init__(self, name, age):
        self.__name = name
        self.__age = age

def getinfo(self):
    info = self.__name + ' ' + str(self.__age)
    print(info)
p = Person('小岳', 16)
print(p.__name)  # 报错
p.getinfo()
print(p.__name)  # 报错
AttributeError: 'Person' object has no attribute '__name'
class Person:
    def __init__(self, name, age):
        self.__name = name
        self.__age = age

    def getinfo(self):
        info = self.__name + ' ' + str(self.__age)
        print(info)

    def setage(self, age):
        self.__age = age


p = Person('小岳', 16)
p.setage(17)
p.getinfo()
年龄: 0
stu姓名:Unkonw,年龄:0
stu姓名:张三,年龄:18
stu姓名:李四,年龄:18
小岳 17
装饰器 @property

插一嘴什么是装饰器??

其实装饰器就是一个闭包,装饰器是闭包的一种应用。
什么是装饰器呢,简言之,python装饰器就是用于拓展原来函数功能的一种函数,
这个函数的特殊之处在于它的返回值也是一个函数,
使用python装饰器的好处就是在不用更改原函数的代码前提下给函数增加新的功能。

装饰器 @property的作用:就是把类的函数属性,封装成类似数据属性

class Person:
    def __init__(self, name, age):
        self._name = name
        self._age = None
    @property
    def age(self):#使用@property修饰了age()方法,这样就是的该方法变成了age属性的getter方法(但是此时age属性是只读的)
        print('get age')
        return self._age
    @age.setter
    def age(self, age):#要想修改age属性的值,还需要添加setter方法,这时就要用到setter装饰器
        print('set age')
        self._age = age
p = Person('小李', 16)
p.age = 17
print(p.age)
get age
None

2.方法

(1)实例方法
共有方法

何时何地都可以调用

私有方法

使用双下划线修饰的方法,只能在类的内部调用

class Person:
    # 定义该类的构造方法
    def __init__(self, name, age):
        self.name = name
        self.age = age
    # 定义该类的成员方法输出对象信息
    def show(self):
        print('姓名:', self.name)
        print('年龄:', self.age)
    def __showage(self):
        print('年龄:', self.age)
    
p=Person('路人甲',18)
p.show()
# Person.__showage()#报错
Person.__showage()
AttributeError: type object 'Person' has no attribute '__showage'
(2)静态方法

什么是静态方法?
静态方法使用装饰器@staticmethod修饰。没有self和cls参数(其他参随意)但是方法体中不能使用类或实例的任何属性和方法。
类和对象都可以调用静态方法。

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age
    @staticmethod
    def getinfo():
         print('Class Person')
# main入口
if __name__ == '__main__':
    p = Person('‘小岳’', 16)
    p.getinfo()
    Person.getinfo()
Class Person
Class Person
#虽然类和实例都可以调用静态方法,但是实例调用静态方法,实际还是类在调用这个静态方法
(3)类方法

什么是类方法呢?
类方法是使用装饰器@classmethod修饰的方法,规定类方法的第一个参数必须是当前类独享,也就是cls代表的是当前的类而不是实例对象,该方法一般约定为“cls”,通过cls传递属性和方法(不能传实例的属性和方法)

class Person:
    info = 'Class Person'
    def __init__(self, name, age):
        self.name = name
        self.age = age
        print(self.info)
    @classmethod
    def getinfo(cls):
        print(cls.info)
# main入口
if __name__ == '__main__':
    p = Person('小岳', 16)
    p.getinfo()
    Person.getinfo()
Class Person
Class Person
Class Person
#虽然类和实例都可以调用类方法,但是实例调用类方法,实际还是类在调用这个类方法。

python面向对象详解 python 面向对象知乎_静态方法_05

4.继承

Python通过()来继承父类,并且子类可以重写并覆盖父类的方法,也可以拥有自己独有的方法。
当要在子类中重写父类的方法时要先引入父类的同名方法,要实现继承的目的有两种方法可以采用。

1.调用未绑定的父类方法

python面向对象详解 python 面向对象知乎_父类_06

2.使用super函数

python面向对象详解 python 面向对象知乎_父类_07

多继承

Java和C#等只能继承一个类,但是Python可以继承多个类,name对象调用寻找方法就有两种形式,一个是深度优先一个是广度优先。Python3.x中均使用广度优先,广度优先的查找也称为砖石继承问题。

A
   / \
  B   C
   \ /
    D
这时候查找顺序是D->B->C->A
```cpp
class Plant():
    def __init__(self):
        print("Enter plant")
        print("Leave plant")

class Fruit(Plant):
    def __init__(self):
        print("Enter Fruit")
        super().__init__()
        print("Leave Fruit")

class Vegetable(Plant):
    def __init__(self):
        print("Enter vegetable")
        super().__init__()
        print("Leave vegetable")

class Tomato(Fruit, Vegetable):
    def __init__(self):
        print("Enter Tomato")
        super().__init__()
        print("Leave Tomato")

tomato = Tomato()


# 输出
# Enter Tomato
# Enter Fruit
# Enter vegetable
# Enter plant
# Leave plant
# Leave vegetable
# Leave Fruit
# Leave Tomato

类中其他常见的方法

1.new

__new__方法在实例创建之前被调用,因为他的任务就是创建实例然后返回该实例,是个静态方法。
也就是__new__是在__init__之前被调用,__new__的返回值(实例)将传递给__init__方法的第一个参数,然后__init__给这个实例设置一些参数。

#__new__至少要有一个参数cls,代表要实例化的类,此参数在实例化时由Python解释器自动提供

#__new__必须要有返回值,返回实例化出来的实例,这点在自己实现__new__时要特别注意,可以return父类__new__出来的实例,或者直接是object的__new__出来的实例

#__init__有一个参数self,就是这个__new__返回的实例,__init__在__new__的基础上可以完成一些其它初始化的动作,__init__不需要返回值

#我们可以将类比作制造商,__new__方法就是前期的原材料购买环节,__init__方法就是在有原材料的基础上,加工,初始化商品环节

内置函数

1.instance函数:

判断一个对象是否是指定类或者指定类的子类。

2.issubclass函数

判断一个类是否是另一个类的子类

3.type函数

用于获取一个对象所属的类

class Person:  # 定义Person类
    pass


class Student(Person):  # 以Person作为父类定义子类Student
    pass

class Flower:  # 定义Flower类
    pass
stu = Student()  # 创建Student类对象stu
f = Flower()  # 创建Flower对象f
print(isinstance(stu, Person))#判断stu是否是Person类或者其子类对象
print(isinstance(f, Person))
print(issubclass(Student, Person))
print(type(f))
True
False
True
<class '__main__.Flower'>```

面向对象的高级应用

动态扩展类与实例

这是什么意思呢?
Python作为一种动态语言,除了可以在定义类时定义属性和方法之外,还可以动态地为已经创建的独享绑定新的属性和方法。

添加动态方法时需要导入types.MethodType
from types import MethodType
class Student:
    pass
def SetName(self,name):
    self.name=name
def SetSno(self,sno):
    self.sno=sno
stu1=Student()
stu2=Student()
stu1.SetName=MethodType(SetName,stu1)#为实例stu1添加方法SetName
Student.SetSno=SetSno#为Student类添加方法SetSno
stu1.SetName("张三")
stu2.SetName("123123")#会报错,因为SetName这个方法只是添加到了实例stu1上而不是类Student上
# AttributeError: 'Student' object has no attribute 'SetName'
stu1.SetSno('2001113')

但是问题来了

# python是动态语言:可以在运行的过程中,修改代码。
# 比如给对象增加属性,给类增加方法等操作。
# 但是如果任意添加或者修改而没有约束的话,对程序的健壮性和安全都没有保障。
# 实际开发中,给实例添加属性进行约束的话,Python允许在定义class的时候,
# 定义一个特殊的__slots__变量,来限制该class实例能添加的属性。
class Person(object):
    def __init__(self, name, age):
        self.name = name
        self.age = age
        #注意这里__slots__函数中的参数用“”包裹
    __slots__ = ("name", "age")
class Student(Person):
    def __init__(self, name, sex, age):
        super().__init__(name, age)
        self.sex = sex
p1 = Person("tom", 18)
# p1.sex = "male"Person添加不了别的属性
#AttributeError: 'Person' object has no attribute 'sex'
stu1 = Student("jack", "male", 22)
print(stu1.name, stu1.sex, stu1.age)
stu1.country = "china"
print(stu1.country)  # 虽然父类设置__slots__属性约束,但是对子类没有约束力。

元类

python中的一切都是对象,那么是对象就有对应的‘类’(Class),或者‘类型’(type)
对象的类型是:类
类的类型是:元类(meta-class),也是type
type的类型还是type

class Student: #定义Student类
   pass
stu=Student() #定义Student类的对象stu
print('stu所属的类是',stu.__class__) #使用__class__属性获取所属的类
print('Student所属的类是',Student.__class__)
stu所属的类是 <class '__main__.Student'>
Student所属的类是 <class 'type'>

鸭子类型

# “当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。”
# 在鸭子类型中,关注点在于对象的行为,能作什么;而不是关注对象所属的类型。

python面向对象详解 python 面向对象知乎_Python_08