python类和对象最全详解(持续修订中)

文章目录

  • 类和对象
    • 面向对象概述
      • 对象
      • 面向对象程序设计的特点
    • 类的定义和使用
      • 定义类
      • 创建类的实例(创建对象)
      • 命名空间
      • 魔术方法
      • 创建类的成员并访问
      • 访问限制
    • 属性
      • 创建用于计算的属性
      • 为属性添加安全保护机制
    • 类与类之间的关系
      • 依赖(关联)
      • 组合(聚合)
      • 继承(实现)
        • 继承的基本语法
        • 淇淇的进阶课堂
        • 方法重写
        • 派生类中调用基类的方法
        • 淇淇的进阶课堂
      • 多继承和多重继承的区别
      • 补充内容`isinstance()及issubclass() `
      • super
    • 封装性
      • 栈:先进后出
      • 队列:先进先出
        • 淇淇的进阶课堂
      • 封装的定义
        • 私有属性
        • 私有方法
      • 破解私有属性
    • 多态性
      • 多态
      • 多态性
    • 设计模式
      • 单例模式
        • 实现单例模式的几种方式
      • 工厂模式
    • 约束
      • 约束的定义
    • 实例方法
    • 类方法
    • 静态方法
      • property
    • 反射.
    • 内置方法
    • 异常机制

类和对象

面向对象概述

什么叫做面向对象?

​ 所谓的面向对象其实就是把属性和方法封装起来,以供重复调用。

比如人:

​ 人的属性:姓名,年龄,性别

​ 人的方法:唱,跳,rap

之前我们没有学习面向对象的时候会这样定义:

伪代码:

姓名 = “帅帅”
年龄 = 18
性别 = “superman”
def():
    print(“唱的真好听”)
def():
    print("跳的真好看")
def rap():
    print("rap世界第一")

这样写有一个弊端,就是只有帅帅,淇淇不服啊。为什么她不出来一下?那么下面我们在实现淇淇的伪代码:

姓名 = “淇淇”
年龄 = 18
性别 = “girl”
def():
    print(“唱的真好听”)
def():
    print("跳的真好看")
def rap():
    print("rap世界第一")

这个时候淇淇的伪代码就完成了,但是淇淇在某些方面是不如帅帅的,这个时候我们要把函数给重新架构一下,成下面这样:

伪代码:

姓名 = “淇淇”
年龄 = 18
性别 = “girl”
def():
    print(“唱的没帅帅真好听”)
def():
    print("跳的没帅帅好看")
def rap():
    print("rap世界第二,搁帅帅后面排着")

到此,我们写完了两个人的,那么如果我们要写很多人呢?这个时候是不是可以把他们给抽象出来,变成一张图纸一样,用的时候直接拿过来用就可以?我们尝试着写成以下的伪代码:

伪代码:

人:
	属性:
        姓名
        年龄
        性别
    方法:
    	唱
        跳
        rap   

这个时候我们发现,只要是人,就可以拥有着写属性和方法。那么这个时候我们可以造出很多这样的人,比如薛之谦,王一博,周杰伦,他们有个共同的特征就是拥有人的属性和方法。

那么在python里面我们是怎样解决的呢?且看我把上面的伪代码实现

class Person:
    def __init__(self,name,age,sex):
        self.name = name
        self.age = age
        self.sex = sex
    
    def sing(self):
        print(f"{self.name}唱的真好听")
    def dump(self):
        print(f"{self.name}跳的真不错")
    def rap(self):
        print(f"{self.name}世界第一")

这个时候我们肯定很奇怪,这不就是把之前的属性和方法前面套了一个class嘛,有什么神奇的?

下面我带你见证这一神奇的时刻:

shuaishuai = Person("帅帅",18,"superman")
shuaishuai.sing()
shuaishuai.dump()
shuaishuai.rap()
#结果:
帅帅唱的真好听
帅帅跳的真不错
帅帅世界第一

到这是不是还是有些不服,这上面的代码完全可以实现,我这写的都多了,是不是?

那么我们在把淇淇也写出来:

qiqi = Person("淇淇",18,"beautifulgirl")
qiqi.sing()
qiqi.dump()
qiqi.rap()
#结果
淇淇唱的真好听
淇淇跳的真不错
淇淇世界第一

这个时候是不是也没多少感觉,只是减少了一点点代码而已啦,那么接下来就是见证帅帅有多帅的时刻了:

zhoujielun = Person("周杰伦",28,"man")
wangyibo = Person("王一博",21,"man")
xuezhiqian = Person("薛之谦",32,"man")
linjunjie = Person("林俊杰",25,"man")

连续生成四个人,而且他们四个人都可以进行唱,跳,rap

你要用以前的方式写,现在都好几十行了,而我这,短短的四行。

这就是面向对象的魅力

对象

什么叫做对象呢?

​ 这道题就是送分题,你的女朋友或者你的男朋友。这里有个狗粮,帅帅&淇淇。单身狗看我博客是不是特别心酸?

​ 开个玩笑,对象就是实际生活中具体的事物。比如你的车就是一个对象,你的小狗就是一个对象,我的手机也是一个对象。我手机老破了,太难过。

python中一切皆对象,linux中一切皆文件

淇淇的进阶课堂:判断下面那个是对象

我的电脑

你的电脑

电脑

鼠标

键盘

手机

我的自行车

Alex的特斯拉

我假装有个特斯拉

什么叫做类?

类是封装对象的属性和行为的载体

类是对象的抽象。刚才我们说了对象就是具体的事物。那么比如我的车,你的车都是对象,这个时候他们有个统一的标准,比如四个轮胎,一个方向盘,四个座椅。而这个时候我们说车的时候,就会知道他会有我们所说的那些属性和方法。

我们住的房子,他就是对象,但是设计房子的图纸,他就是类。他可以设计成各种各样的,然后由我们勤劳的劳动人民去实现。比如天津的网红图书馆,天津的失恋博物馆(好像在这说这个有点尴尬,毕竟俺是有对象的人)。

那么我们刚才说的什么馆,抽象出他们的相同点,他们统一有一个专有名词,叫做馆。这个就是类。

总结:类是对象的抽象,对象是类的具体实例。

面向对象程序设计的特点

面向对象的三大特性:封装,继承,多态

封装:

我们使用电视遥控器,只需要知道按上下就可以进行换台操作,而不知道遥控器是具体怎么实现的,这就是封装。所谓封装,就是把具体实现隐藏起来,别人使用的时候只给固定的结构进行调用。

继承:

继承跟我们现实生活中的继承类似,你爸有好几套房,看你长大了,老在外面租房住,感觉不太好,送了你一套。这就是继承,而面向对象的继承比现实生活中强了不知道无数倍。继承来的东西你的父类还是存在。也就是说,你爸有十套房,你继承之后你也会有十套房,而且你爸还不会少。不像我们现实世界,你爸给你之后,你爸就没了这十套房。

多态:

一种事物的不同表现形态。python中是非常喜欢鸭子模型的,就是有个鸟,它会游泳,它会跑,它会叫,它就是鸭子。关于多态请看后面的多态详细描述。

类的定义和使用

类表示具有相同属性和方法的集合,使用类就是创建对象。

定义类

固定结构:

class 类名:  #类名一般使用大驼峰命名
    类属性
    类方法

写好了类之后并不能直接使用。就跟房子的图纸一样,你写好并不能进行住,需要盖好才可以。

创建类的实例(创建对象)

class Person:
    def __init__(self,name,age,sex): #self:代表当前对象 剩下的三个属性在类创建对象的时候必须实现
        #类属性,可以在__init__中添加,也可以在其他方法里面添加
        self.name = name
        self.age = age
        self.sex = sex
	
    #类方法
    def sing(self):
        print(f"{self.name}唱的真好听")
    def dump(self):
        print(f"{self.name}跳的真不错")
    def rap(self):
        print(f"{self.name}世界第一")
        
shuaishuai = Person("帅帅",18,"superman")

删除对象属性:del 对象名.属性名不建议删除

__init__魔术方法

当类中写了这个方法的时候,第一个参数必须是self,代表当前对象,然后第二个参数开始就随意,但是这些参数在创建对象的时候必须一一实现。

创建对象的时候自动调用__init__方法

命名空间

类空间和对象空间

类也遵从自上而上执行,所以如果类里面有同名的方法,前一个方法会被后一个方法覆盖。

然后对象初始化的时候,会调用类覆盖好的方法

# 创建一个类,能够自动统计当前类创建了多少个对象
class Person():
    num = 0
    def __init__(self):
        Person.num += 1
print(Person.num)
Person()
Person()
Person()
Person()
print(Person.num)

魔术方法

python中前后用两个下划线包起来的叫做魔术方法

python3中的魔术方法有:

常用的魔术方法:
__init__:
1.用来构造初始化函数,用来给对象进行初始化属性,所以不需要返回值
2.创建对象的时候自动调用
3.自定义类如果不定义的话,默认调用父类object的,同理继承也是,子类若无,调用父类的,若有,调用自己的

class Animal:
    def __init__(self):
        print("init初始化方法,没有调用父类object")

Animal()
#结果:
init初始化方法,没有调用父类object

__new__:
1.用所给类创建一个对象,并且返回这个对象
2.因为是给类创建实例,所以至少传一个参数cls,参数cls代表代表要实例化的类,此参数在实例化时用python解释器自动提供
3.在类实例化时内部创建类实例的函数,并且返回这个实例,所以它是实例时最先被调用的方法,一般不要人为定义该方法
4.因为要创建实例返回实例,所以要有返回值。return父类__new__出来的实例,或者直接是object的__new__出来的实例

__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__

创建类的成员并访问

类的成员主要有属性和方法,属性可以随意,方法的第一个参数必须是self

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

    def sing(self):
        print(f"{self.name}唱的真好听")
    def dump(self):
        print(f"{self.name}跳的真不错")
    def rap(self):
        print(f"{self.name}世界第一")
        
shuaishuai = Person("帅帅",18,"superman") #初始化
#访问成员方法
shuaishuai.sing()
shuaishuai.dump()
shuaishuai.rap()

访问类中的属性和方法只需要对象使用成员运算符来进行调用

类属性通过类来访问,对象属性通过对象来访问

访问限制

如果想把某些属性给隐藏起来,不想让外界直接访问,可以在属性名前面加两个下划线。比如帅帅不想直接让别人知道他的年龄或者修改他的年龄:

可以使用单下划线,双下划线,首尾双下划线来限制访问权限

单下划线开头的是protected类型的成员,只允许类本身和子类进行访问,不能使用from xxx import ccc进行导入

双下划线只能由定义了该属性或方法的类调用,而不能由类的对象调用,类的对象如果想调用,必须使用set/get方法

首尾双下划线是系统定义的特殊方法,一般是系统定义的名字

class Person:
    def __init__(self, name,sex):
        self.name = name
        self.__age = None
        self.sex = sex

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

    def get(self):
        return self.__age

    def sing(self):
        print(f"{self.name}唱的真好听")

    def dump(self):
        print(f"{self.name}跳的真不错")

    def rap(self):
        print(f"{self.name}世界第一")


shuaishuai = Person("帅帅","superman")  # 初始化
#这个时候初始化帅帅的年龄就会报错,可以使用set方法来赋值,get方法取值
shuaishuai.set(18)
print(shuaishuai.get())

属性

创建用于计算的属性

有的时候我们需要创建对象的时候就进行给属性赋值,但是这样的方法往往是不安全的,会直接修改我们类中的属性,这样的话安全性不高。比如你的年龄是18岁,但是这个时候有用户进行恶作剧,直接将其修改为600岁,你是不是乐坏了?但是你感觉现实吗?

python中使用@property将一个方法转换为属性,从而实现用于计算的属性,将方法转换为属性后,可以直接通过方法名来访问,而不需要加括号。

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

    @property
    def func(self):
        if self.age < 150 and self.age > 0:
            print(self.age)
        else:
            print("想啥呢?")

shuaishuai = Person("帅帅",18)
shuaishuai.func  #func这个属性不能对其赋值,因为他本质也是一个函数

@property可以将属性设置为只读属性

为属性添加安全保护机制

为此,python中有了私有属性和私有方法供大家使用。私有属性就是在属性前面加两个下划线,然后给这个私有属性给予set和get方法,这样对象调用的时候就只能通过set方法来进行赋值,get方法来获取值

class Person:
    def __init__(self, name,sex):
        self.name = name
        self.__age = None
        self.sex = sex

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

    def get(self):
        return self.__age

类与类之间的关系

一个一个的类就跟我们现实世界中的模板一样,只有把他们全部关联起来,才会使我们做事更加高效。比如轮胎跟发动机组合成了车,零食跟零食袋组合成了零食一样。类和类的组合,一般分为以下三种关系:

依赖(关联)

一个动作执行的时候,需要另一个动作来帮忙。比如打开电脑,帅帅去玩电脑或者帅帅去打王者

依赖就是我用你,你需要我。

比如帅帅玩电脑,电脑需要帅帅玩

#依赖
class Person:
    def run(self):
        print("我没事干")

class Computer:
    def play(self,tool):
        tool.run()
        print("我是电脑,玩我")


class Phone:
    def play(self,tool):
        tool.run()
        print("我有王者荣耀,来玩啊")

shuaishuai = Person()
dnf = Computer()
dnf.play(shuaishuai)   #依赖是给一个类的对象的方法给另一个对象

wangzhe = Phone()
wangzhe.play(shuaishuai)

一个类使用某个类中的某个方法

这种情况下,类与类之间的关系是最轻的,可以随时替换。

依赖就是一个类的对象的方法自己完不成一些功能,需要另一个对象来完成,把他加载到此方法中。就是依赖

组合(聚合)

一个类需要某个类的具体对象去做一些事情,这就是组合。轮胎和发动机组合成了车一样。

组合可以一对一,也可以一对多

一个类与另一个类没有共同点,但是他们之间有关系,这就是组合

组合就是我需要什么,但是我没有这些东西,我就拿你。

class Car:
    def __init__(self,name,power = None):
        self.__name = name
        self.__power = power

    def set_power(self,power):
        self.__power = power

    def zhuang_Tread(self):
        print(f"{self.__name}装好了{self.__power.get_name()}的轮胎")

    def saiche_power(self):
        print(f"{self.__name}装好了{self.__power.get_name()}的发动机")

class Tread:
    def __init__(self,name):
        self.__name = name

    def set_name(self,name):
        self.__name = name

    def get_name(self):
        return self.__name

class Engine:
    def __init__(self,name):
        self.__name = name

    def set_name(self,name):
        self.__name = name

    def get_name(self):
        return self.__name


tread = Tread("牛逼牌")
engine = Engine("赛车")
car = Car("奔驰",tread)
car.zhuang_Tread()
car.set_power(engine)
car.saiche_power()

#分析:
是不是发现了有很多的重复代码,而且极其麻烦,不如用继承好。刚开始用,还是没有那么的得心应手,我还的努力。

将一个类的对象封装到另一个类的属性中进行一些操作

淇淇的进阶课堂

老师和学生模型(老师对学生是一对多,学生对老师是一对一)
创建教师类和学生类
教师类有姓名和学生列表两个属性
教师类有添加学生的方法(添加的学生是具体对象)
教师类有显示对应学生姓名和学号的方法
学生类有学号/姓名/年龄属性
创建多个学生,并添加到某位教师的学生列表中
打印该教师的所有学生学号

继承(实现)

一般我们看到继承这两个字都有一种一夜暴富的心态。比如我们看的西虹市首富,猛虫过江,都是有了巨额的财富,令继承人过上了幸福的生活。那么在我们的Python中,继承究竟是怎么样的呢?在python中子类会拥有父类的属性和方法,但是不会继承私有属性和私有方法。被继承的类叫父类或基类,继承的类叫做子类或派生类。

继承的基本语法
#固定结构:
#父类
class Person(object): #括号里面写要继承的类
    def __init__(self, name,sex):
        self.name = name
        self.__age = None
        self.sex = sex

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

    def get(self):
        return self.__age

    def sing(self):
        print(f"{self.name}唱的真好听")

    def dump(self):
        print(f"{self.name}跳的真不错")

    def rap(self):
        print(f"{self.name}世界第一")

#子类
class Boy(Person):
    pass

shuaishuai = Boy("帅帅","superman")
shuaishuai.sing()

#结论:
可以看出,子类Boy什么都没有写,只是继承了一下父类Person,他就拥有了父类的属性和方法,但是私有属性没有被继承。
淇淇的进阶课堂
老师和学生模型(老师对学生是一对多,学生对老师是一对一)
创建教师类和学生类
教师类有姓名和学生列表两个属性
教师类有添加学生的方法(添加的学生是具体对象)
教师类有显示对应学生姓名和学号的方法
学生类有学号/姓名/年龄属性
创建多个学生,并添加到某位教师的学生列表中
打印该教师的所有学生学号

组合是指在新类里面创建原有类的对象,重复利用已有类的功能“has-a”关系

而继承允许设计人员根据其它类的实现来定义一个类的实现“is-a”关系

方法重写

方法重写就是子类继承父类的方法,然后把方法体进行重写。

比如父类是房子的图纸,因为每个人对美的定义不一样,你感觉你爸弄得这个图纸不适合你,你又不想重新弄。就把比如卧室进行重新设计一下,床啥的就不换了。这就是方法重写

class Person(object):
    def __init__(self, name):
        self.name = name
    def sing(self):
        print(f"{self.name}唱的真好听")
   
#子类
class Boy(Person):
    def sing(self):
        print(f"{self.name}最帅")

shuaishuai = Boy("帅帅")
shuaishuai.sing()    
#结果:帅帅最帅
这个时候我们可以发现,原来我们不重写sing方法的时候,他调用的是父类的sing方法,当我们在子类中进行重写之后,他显示的就是子类的方法。
由此可以得出查找顺序:对象调用某个属性或者方法的时候,会先在当前类中进行查找,如果找不到,就去他的父类对象今进行查找。

深度优先和广度优先

派生类中调用基类的方法

子类如果有自己的初始化属性,也就是有魔术方法__init__的时候,子类不会自动执行父类的方法,必须使用super来进行调用,获取父类的初始化属性

使用super关键字调用父类的方法

子类如果编写了自己的构造方法,但没有显式调用父类的构造方法,而父类构造函数初始化了一些属性,就会出现问题

解决方式:调用超类构造方法,或者使用super函数 super(当前类名, self).init()

super在Python3和python2中的区别:

Python3可以使用直接使用 super().xxx 代替 super(Class, self).xxx

super钻石继承

super的内核 mro方法:返回的是一个类的方法解析顺序表(顺序结构)
我们定义的每一个类,Python 会计算出一个方法解析顺序(Method Resolution Order, MRO)列表,这也是super在父类中查找成员的顺序,它是通过一个C3线性化算法来实现的

我们可以使用下面的方式获得某个类的 MRO 列表
类名.mro() 或者 对象名.__class__.mro()
淇淇的进阶课堂
练习:
有一个动物类,猫狗两个类都继承了动物,动物类拥有动物的属性,定制猫狗类的个性属性

动物都有吃的方法,动物在吃东西之后会打印自己的血量值和智力值,但是猫和狗吃的东西不一样

猫狗在吃了食物之后,猫会增加血量值,狗会增加智力

多继承和多重继承的区别

多继承是子类不断的继承父类,依次类推,最后的孙子类拥有父类跟子类的方法

#多继承
class Person(object):
    def run(self):
        print("人会跑")

class Boy(Person):
    def like_girl(self):
        print("男孩喜欢女孩子")

class Girl(Boy):
    def like_boy(self):
        print("女孩子也喜欢男孩子")

class Kid(Girl):
    def kids(self):
        print("孩子是男孩跟女孩的爱情结晶")

kid = Kid()
kid.run()
kid.like_girl()
kid.like_boy()
kid.kids()

#结果
人会跑
男孩喜欢女孩子
女孩子也喜欢男孩子
孩子是男孩跟女孩的爱情结晶

可以看出,多继承就是最后的一个类拥有上面的类的属性和方法(除了私有属性和私有方法)

多重继承逻辑不行的人就别看了。

补充内容isinstance()及issubclass()

isinstance()issubclass() 
isinstance() 用于检查实例类型:isinstance(对象,类型)
issubclass() 用于检查类继承:issubclass(子类,父类)

super

使用多继承,会涉及到查找顺序(MRO)、钻石继承等问题
钻石继承
单继承时 类名.init()的方式和super().init()方式没啥区别

但是使用 类名.init()的方式在钻石继承时会出现问题

super的内核 mro方法:返回的是一个类的方法解析顺序表(顺序结构)
我们定义的每一个类,Python 会计算出一个方法解析顺序(Method Resolution Order, MRO)列表,这也是super在父类中查找成员的顺序,它是通过一个C3线性化算法来实现的

我们可以使用下面的方式获得某个类的 MRO 列表
类名.mro() 或者 对象名.class.mro()

封装性

栈:先进后出

栈在内存中保存对象的引用

堆在内存中保存对象

总结:对象在栈中保存,在堆中被引用

队列:先进先出

淇淇的进阶课堂

实现栈和队列

封装的定义

封装就是把一个个的属性和方法隐藏起来,只留下具体的接口供下一个人使用

我们最常见的封装就是遥控器,你不需要知道遥控器是怎么装的,直接拿来开始换台就行。比如动物世界。

私有属性

私有属性就是在属性前面加get/set方法,然后再当前类中进行调用和实现,外界只能使用这个属性的set/get方法来操作他。而且不能被子类继承

私有方法

私有方法跟私有属性一样,在方法前面加两个下划线

破解私有属性和私有方法
_类名__属性

破解私有属性

破解私有属性和私有方法:
在名称前加上 _类名,即 _类名__名称(例如a._A__N)

其实加双下划线仅仅是一种变形操作
类中所有双下划线开头的名称如__x都会自动变形成:_类名__x的形式

多态性

多态

一个变量可以有不同的数据类型

一个事物的不同表现形态,具体表现在比如狗,有柯基,二哈,还有淇淇(淇淇那么可爱,哈哈哈)

python天生支持多态

多态性

多态基础上,定义统一的接口。不同的类对象作为参数使用时,得到的结果不同

python著名的鸭子模型:

只要你会某一类功能,你就是一只鸭子。

比如会游泳,会飞,会跑,好了,你是鸭子,周黑鸭的鸭子。

对比:
多态强调的是:一类事物 不同的形态
多态性强调的是:同一操作,作用对象不同,表现出不同实现方式(只关心行为结果)
练习
创建汽车类(Car)含实例属性颜色red,对象方法run,功能是打印XX颜色小汽车在跑。
创建猫类(Cat)含实例属性名字name,对象方法run,功能是打印猫咪XX在跑。
实例化汽车类颜色为红色,实例化猫类,使用公共函数调用对象方法

设计模式

设计模式就是前人总结的经验,解决某一类问题的具体方法。比如我们肯定都做过算术题,知道首先做括号里面的,然后在做乘除,最后加减。还有我们的城市群落的形成,也是先盖新城,拉动经济,在弄旧改。所以我们学习设计模式之后,以后解决某一类问题,都不需要再重复造轮子。

单例模式

有时通过单一对象方便集中管理资源
单例模式是保证一个类仅有一个实例的设计模式

保证了在程序的不同位置都可以且仅可以取到同一个对象实例:如果实例不存在,会创建一个实例;如果已存在就会返回这个实例。

总结:某一个类只有一个对象,可以处处调用

学习的文章

实现单例模式的几种方式
  1. 使用模块

    1. python的模块是天然的单例模式,模块第一次导入时,会生成.pyc文件,第二次导入时,他只会加载.pyc文件,而不会再次执行模块代码。
  2. 使用装饰器

  3. 使用类

  4. 基于魔术方法new(***)

    当实例化一个对象时,是先执行了类的__new__方法(我们没写的时候默认调用object.__new__)然后再实例化类的__init__方法

    class Singleton(object):
        instance= None
        def __new__(cls, *args, **kwargs):
            if not cls.instance:
                cls.instance = object.__new__(cls)
            return cls.instance
    
    s1 = Singleton()
    s2 = Singleton()
    
    print("s1的地址:{},s2的地址:{}".format(id(s1), id(s2)))
    结果:
    s1的地址:1085113828968,s2的地址:1085113828968
    
  5. 基于metaclass方式实现

    1. 相关知识
    2. 实现单例模式

工厂模式

python中不需要创建类,创建子类来实现工厂模式,可以直接使用

class Factory(object): 
    def get_person(self, name, gender='M'):
        if gender == 'M':
            return Male(name)
        if gender == 'F':
            return Female(name)

约束

约束的定义

约束就是一些约定,我们以后早晨八点来上课,晚上十点回家。这就是一种约束。在学校我们听老师的话,在家里听爸爸妈妈的话,在社会上听领导的话。什么时候不听话呢?就是你成了爷爷的时候,哈哈哈。现在既然咋们还没有是爷爷的时候,就按照规定来干。

  1. 在父类建立一种约束(通过抛出异常)

    class Animal():
        def eat(self):
            raise Exception("子类必须拥有eat方法")
    
    class Dog(Animal):
        def eat(self):
            print("dog eat")
    
    class Cat(Animal):
        def eat(self):
            print("cat eat")
    
    class Bir(Animal):
        def bireat(self):
            print("bir eat")
    
    def change(tool):
        tool.eat()
    
    dog = Dog()
    change(dog)
    bir = Bir()
    change(bir)
    
  2. 引入抽象类的概念

    from abc import ABCMeta,abstractmethod
    class Airer(metaclass=ABCMeta):
        @abstractmethod
        def airfly(self):
            pass
    #结论:
    抽象类在父类中抽象完毕之后,必须在子类中重写,不然会报错。
    

实例方法

类中定义的普通方法

class Person:
    def func(self):
        pass
只有实例化之后才可以使用,接收的第一个参数一定为对象本身

类方法

指一个类中通过@classmethod修饰的方法

第一个参数必须是当前类的对象,该参数名一般约定为cls,通过它来传递类的属性和方法。
调用:实例对象和类对象都可以调用
类方法是将类本身作为对象进行操作的方法

通过使用类方法可以在不创建对象的时候直接进行操作。比如有的时候我们不想要实例化对象,而执行一些操作的时候,完全可以使用

应用场景:当一个方法中只涉及到静态属性的时候可以使用类方法(类方法用来修改类属性)

#使用类方法对商品进行统一打折
#分析:商场里面客户购买东西,打几折的时候,用户自己算往往不是很快,而且还很累,这个时候就需要直接计算出打折后的价格。客户只需要调用这个类方法就可以。
class Shop:

    def __init__(self,price):
        self.__price = price

    @property
    def func(self):
        return self.__price * 0.5

s = Shop(100)
print(s.func)
print(Shop.func)

#进阶版
class Shop:
    __discount = 1  #折扣价

    def __init__(self,name,price):
        self.__name = name
        self.__price = price


    @property
    def discount_money(self):
        self.__discount = 0.5

    @property
    def finally_money(self):
        return self.__price * self.__discount

s = Shop("橘子",5)
s.discount_money
print(s.finally_money)

Shop("苹果",10)
Shop.discount_money
print(Shop.finally_money)

#类方法进阶版
class Shop:
    __discount = 1  #折扣价

    def __init__(self,name,price):
        self.__name = name
        self.__price = price


    @classmethod
    def discount_money(cls,num):
        cls.__discount = num / 10

    @property
    def finally_money(self):
        return self.__price * self.__discount

s = Shop("橘子",5)
s.discount_money(8)
print(s.finally_money)

类方法说了这么多,具体应用场景还是不太明确,还需要接着研究。

静态方法

使用@staticmethod修饰的方法

参数随意,没有selfcls参数

方法体中不能使用类或实例的任何属性和方法

实例对象和类对象都可以调用

静态方法是类中的函数,不需要使用实例。也就是说不需要进行实例化就可以调用

静态方法一般定义后就不需要再进行修改,用来写一些固定的操作

应用场景:一般是与类和对象无关的代码

#静态方法
class Person:

    @staticmethod
    def func():
        print("""
        1.搞嘛?
        2.不知道
        3.先记下来吧
        4.记下来不知道应用场景
        5.我好累
        """)
Person.func()

property

使用property修饰的方法可以直接使用对象.方法名进行调用,不需要给方法加括号。

应用场景:商场打折,计算周长,面积等

一般具有三种访问方式:获取,修改,删除

只有在属性定义property后才能定义setter,deleter

方法一

class Foo:
    @property
    def AAA(self):
        print('get的时候运行我')
    @AAA.setter
    def AAA(self,value):
        print('set的时候运行我')
    @AAA.deleter
    def AAA(self):
        print('delete的时候运行我')

方法二

class Foo:
    def get_AAA(self):
        print('get的时候运行我啊')

    def set_AAA(self,value):
        print('set的时候运行我啊')

    def delete_AAA(self):
        print('delete的时候运行我啊')
    AAA=property(get_AAA,set_AAA,delete_AAA) 
#内置property三个参数与get,set,delete一一对应

只有@property定义只读,加上@setter定义可读可写,加上@deleter定义可读可写可删除

反射.

反射是指计算机程序在运行时(Run time)可以访问、检测和修改它本身状态或行为的一种能力。用比喻来说,反射就是程序在运行的时候能够“观察”并且修改自己的行为。

python面向对象中的反射:通过字符串的形式操作对象相关的属性。python中的一切事物都是对象(都可以使用反射)

反射就是通过字符串的形式,导入模块;通过字符串的形式,去模块寻找指定函数,并执行。利用字符串的形式去对象(模块)中操作(查找/获取/删除/添加)成员,一种基于字符串的事件驱动!

有四个可以实现自省的函数:hasattr,getattr,setattr,delattr

user = User()
while True:
    choose = input('>>>').strip()
    if hasattr(user,choose):
        func = getattr(user,choose)
        func()
    else:
        print('输入错误。。。。')

内置方法

__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__

异常机制

异常(Exception)是一个事件,该事件可能会在程序执行过程中发生,影响了程序的正常执行
raise语句显式的抛出异常
Python解释器自己检测到异常并引发它

异常的固定结构:

try:
     被检测的代码块
except 异常类型:
     try中一旦检测到异常,就执行这个位置的逻辑

自定义异常:
python中提供的错误类型可能并不能解决所有错误

如果以后你在工作中,出现了某种异常无法用已知的错误类型捕获(万能异常只能捕获python中存在的异常),那么你就可以尝试自定义异常,只要继承BaseException类即可

自定义异常:
class HelloError(Exception):
    def __init__(self,n):
        self.n=n
try:
    n=input("请输入数字:")
    if not n.isdigit():
        raise HelloError(n)
except HelloError as hi:
    print("HelloError:请输入字符。\n您输入的是:",hi.n)
else:
    print("未发生异常")

你可能感兴趣的:(培训的日子,类和对象,python)