面向对象

面对对象

什么是面对对象

在学习面对对象之前需先明确面向过程

在学习面向对象之前我们写的任何代码都是面向过程的

什么是面向过程?面向过程是一种编程思想

即面向 朝向过程去编写代码,在编写代码是要时刻想着过程

过程是什么?解决问题的步骤 流程,即第一步干什么

其目的是将一个复杂的问题,拆分为若干的小问题,按步骤一一解决

也可以说将一个复杂性的问题,流程化(为其制定一个固定的实现流程)

面向过程编程的优缺点

​ 优点:复杂的问题简单化

​ 缺点:由于其实现流程是固定的,一但中间某一个步骤发生了修改,将导致整体都需要修改,牵一发而动全身

实现某个功能固定流程,再在原来的基础上增加新功能,就需要重新设计一遍,扩展性差,维护性差

由于面向过程所存在的缺点,才催生了新的编程思想

面向对象

​ 是一中编程思想,即编写代码的方法

​ oop ,object—oriented programming的简称,就是面向对象的英文缩写

​ 将程序看做是一堆对象的集合,实现功能的方式就是对象之间交互来实现

什么是对象

​ 一切皆对象

​ 对象指的是具备某些特征与进的结合体,是实实在在存在的具体物体(描述完就能找到)

什么是类

​ 类就是类型,类别,分类

​ 类是一个抽象概念,不是实际存在的,是根据一些具备相同特征和技能的对象 抽取到的

类与对象之间的关系

​ 类事包含一系列相同特征和技能的对象

​ 对象是属于某个类的实例

先有类还是先有对象

​ 在生活中是先有对象再根据对象特征和技能划分类

在程序中是先有类还是先有对象

​ 程序员在程序中相当于上帝 需要什么就创造什么

​ 在程序中先有类,才能通过类来产生对象,要先确定对象具备什么特征和行为才能产生对象

面向对象的优缺点

​ 优点:

​ 1、不用考虑繁琐的实现步骤,从一个操作工变成了指挥者。

​ 2、扩展性高,当需要一个新功能时,加一个具备新功能的对象,命令它去完成任务

​ 3、各个对象之间耦合度低,当一个一个对象出现了问题,不会对其他对象产生影响

​ 4、可维护性高

​ 缺点:

​ 1、面对对象的复杂度比面向过程高

​ 2、无法预知执行结果,就象lol的游戏过程,一定不能把过程写死,每个英雄的行为是不固定的

​ 使用场景:需要较高的扩展性时,(直接与用户发生交互的程序,例如qq,微信)

​ 对于不需要扩展性的程序而言,使用面对对象反而增加了复杂度

​ 为什么要使用面对对象,因为在目前情况下,很多程序与用户直接打交道而用户的需求千变万化,所以对扩展性要求非常高

​ 面对对象编程的本质就是使用使用不同的对象来完成

类和对象

在计算机创建一个人类对象

​ 要得到对象必须要先告诉计算机,这个对象具备是吗特征和行为,所以需要先定义类

注:类名要按照大驼峰的方式来书写ThisIsPerson 每个单词的首字母都大写

class Person:
	pass

​ class就是定义一个类

在类中面向对象的特征和行为

class Person:
	#用变量来描述特征
	name = "李四"
	sex = "man"
	age = 20
 	#得到对象 通过调用类也称之实例化 或创建对象
obj = Person()
print(obj)
#输出结果<__main__.Person object at 0x00000279452B7C50>
#使用对象的实现(说的就是特征)
print(obj.name)
print(obj.age)
print(obj.sex)

表示模块名为main 其包括一个Person类 通过Person类产生了一个对象地址为0x00000279452B7C50

这是一个Person类的对象,其地址为0x00000279452B7C50

class Student:
	number = "007"
	name = "嘉文"
	sex = "man"
	age = 20
 # 学生的学校 由于每个学生的学校都是相同 所以将其放到类中
    school = "Tsinghua"



# 创建了两个学生对象
stu1 = Student()
stu2 = Student()

print(stu1)
print(stu2)
# 每个对象内存地址都是不同的 , 在创建对象时,计算机会申请一个新的内存空间,并将对象中的内容存进去

print(id(stu1.name))
print(id(stu2.name))

# 由于name的值时声明在类中的,所以每个对象使用的都是同一份


print(stu1.name)
print(stu2.name)

# 为对象单独制定属性
stu1.name = "韩信"
stu2.name = "陈大炮"


print(stu1.name)
print(stu2.name)


# 每个对象的name属性都不同,则意味需要给每个对象单独指定name

存储属性的位置有两个 一个是类中,还有一个是对象中

当每个对象的某个特征都相同时则放到类中

当每个对象的某个特征多不同时则放到对象中

通过_ dict _ 可以获取一个对象中包含的内容

stu1.age = 30
print(stu1.__dict__)
print(stu2.__dict__)
#获取类中包含的内容
print(Student.__dict__)

属性的访问顺序

class Car:
    c_type = "破鞋"
    color = "red"
    price = 400000


c1 = Car()
c2 = Car()

print(c1.__dict__)
print(c2.__dict__)
print(c1.c_type)

# 当对象中不存在是会到类中去找

c1.c_type = "法拉利"
print(c1.__dict__)
print(c2.__dict__)
print(c1.c_type)

print(c2.c_type)
# 如果对象中存在这个属性,优先访问对象中的属性
print(Car.__dict__)

查找顺序 先查对象 再查类

当创建一个类的时候 会产生名称空间,存储类中名称和值的绑定关系
当创建一个对象的时候 会产生名称空间,存储对象中名称和值的绑定关系
类还有另一个作用 就是 作为对象的模板,所有属于同一个类的对象,都具备类中的公共内容

即使我们什么都不写 类中也存在一些自带的属性,是从父类得到的(继承会详细讨论)

初始化函数

class Student:
    # 定义一个函数 用于为对象设置属性
    def set_attr(obj, name, sex, age):
        obj.name = name
        obj.sex = sex
        obj.age = age
    pass


stu1 = Student()
# 指定属性
stu1.name = "张无忌"
stu1.sex = "man"
stu1.age = 18


stu2 =Student()
stu2.name = "周芷若"
stu2.sex = "woman"
stu2.age = 78




# stu3 = Student()
# stu4 = Student()


# set_attr(stu3,"灭绝","woman",88)
# set_attr(stu4,"金毛狮王","man",20)


# print(stu3.__dict__)
# print(stu4.__dict__)
# set_attr这个函数目的是用于设置对象的属性 ,如果没有对象则该函数没有存在的意义,也就是初始化函数与类应该是一个整体
# 应该讲这个函数放到类中
stu3 = Student()
stu4 = Student()


Student.set_attr(stu3,"金毛狮王","man",80)
print(stu3.__dict__)


Student.set_attr(stu4,"张全蛋","man",28)
print(stu4.__dict__)


# 现在已经简化了代码 但是对象的创建和初始化步骤是分开的, 通常对象一旦创建 就应该进行初始化,所以最好时将创建与初始化进绑定

stu5 = Student()
print(stu5)
# 作为一个人 一旦出生 性别必须要指定
# 带有__开头__结尾的函数 是一些特殊内置函数,会在某个时间点自动触发执行
class Person:
    # 初始化函数名称是固定  该函数会在调用类是时自动执行,self参数必须有,表示要进行初始化的对象,系统会自动传值
    def __init__(self,name,age):
        print("执行了 __init__")
        print(self)
        self.name = name
        self.age =age

p1 = Person("张三丰",80)

print(p1.__dict__)

p2 = Person("李狗蛋",20)
print(p2.__dict__)

# init 函数用于初始化对象,它会在创建对象时,自动执行,并传入调用类时传递的参数,第一个参数表示要初始化的对象本身,
# self(第一个)参数不需要手动传递
# self表示对象自己 是一个形式参数,名字可以随便取,但是不建议修改

绑定方法

绑定方法
什么是绑定 把两个东西捆绑在一起
什么是方法 方法 就是 函数
​ 函数是专业术语,不好理解,面向对象编程思想,是要我们模仿现实生活中的抽象概念,为了方便理解就把函数称之为方法

绑定方法就是 把对象与函数进行绑定
为什么要把把对象与函数进行绑定
​ 调用函数 就变成了调用对象的方法

对象本质上就是一种存放数据的容器
函数是用于处理数据的代码
绑定方法就是将数据与处理数据的函数绑定在一起

最终问题是 为什么要把数据与处理数据的函数绑定在一起?

如何使用绑定方法

class Student:
    school = "BeiJing"

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

    def learning(self):
        print("正在学习..")

    def sayHI(self):
        print("hello my name is %s my age:%s my sex:%s" % (self.name,self.age,self.sex))

    # 默认情况下 在类中定义的函数都是绑定方法,共同点是,都会讲对象作为第一个参数self



stu1 = Student("一个学生","man",18)

stu1.sayHI()

Student.sayHI(stu1)

# 当用用对象来调用类中的方法时,默认把对象传入方法中
# 而用类名来调用时,则需要手动传入对象


print(stu1.sayHI)
#<bound method Student.sayHI of <__main__.Student object at 0x000001784F889C50>>
# 这是一个绑定方法,本质上是Student类中的sayHI函数 现在把这个函数绑定给了地址为0x000001784F889C50的对象



stu2 = Student("李四","女",19)
stu2.sayHI()
print(stu2.sayHI)

# 只要拿到对象 就同时拿到了数据和处理数据的方法

为设么要有绑定

第一个问题传递参数,必须手动传递,很有可能传参顺序而发生错误
第二个问题每次处理数据 都需要手动传参数
第三个问题 当要处理的数据特别的多 就不能再定义为变量了 你可以使用列表出来存储要处理的数据
但是 每次处理 都需要先获取数据 在传递给处理数据的函数
所以将 要处理的数据与 处理数据的函数进行绑定

如何绑定? 就得用对象了

绑定给类方法

class Student:
    school = "beijing"

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

    # 绑定方法分为两种 一种是绑定给对象的,一种绑定给类的



    # @classmethod
    # def print_school_name():
    #     print("学校名为:%s" % Student.school)

    # 绑定给类的方法 使用一个装饰器叫classmethod,必须有一个参数,表示当前类,参数名也可以自己定义,建议不要修改
    @classmethod
    def print_school(cls):  # 输出类里面叫school的属性
        print(cls.school)

    # def print_school2(self):  # 输出类里面叫school的属性
    #     print(self.school)

    # 这是绑定给对象的方法
    def sayHello(self):
        print(self.name, " 说: 你好")



# Student.print_school_name()

Student.print_school()
Student.print_school()
stu1 = Student("印度阿三","woman",20)
stu1.print_school()


stu10 = Student("赵六","男",20)
Student.print_school2(stu10)

对象绑定方法 可以使用对象来调用 也可以使用类名来调用
在对象调用时会自动传入对象自己
类调用时不会自动传参

类的绑定方法,对象和类都能调用,并且都会自动传入这个类

一个方法到底应该绑定给对象还是帮对给类?
​ 当要处理的数据包含在类中时,就应该绑定给类
​ 当要处理的数据包含在对象中时,就应该绑定给对象

# 有一个Dog类  每一个Dog对象都应该会叫 会跑  请用面向对象来完成


class Dog:
    def __init__(self,nikename,gender,age):
        self.nikename = nikename
        self.gender = gender
        self.age = age

    def run(self):
        print("不好了 ",self.nikename,"跑了 ")

    def bark(self):
        print("听",self.nikename,"在瞎叫...")

d1 = Dog("大金毛","母的",2)
d2 = Dog("大黄","公的",3)

d1.run()
d2.bark()

类的绑定方法和对象的绑定方法的相同与不同
相同点:
​ 1.都会自动传值
​ 2.都可以被类和对象调用

不同点:
​ 1.对象绑定方法再对象调用时 传的是对象自己 而类绑定方法字自动传的是类自己
​ 2.第一个参数 个cls 一个叫self

非绑定方法

class Teacher:

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

    # @staticmethod 用于定义个非绑定方法
    @staticmethod
    def test_func(num):
        print("test_func run!")
        print(num)

Teacher.test_func(1)

t1 = Teacher("矮根","男")
t1.test_func(100)
print(t1.test_func)

什么是非绑定方法 在类中 即不绑定给类 也不绑定给对象
特点:没有自动传参数的效果 ,类和对象向都能调用,就是一个普通函数
当你的这个功能不需要访问类的数据 也不需要访问对象的数据,就可以作为一个非绑定方法
使用场景较少

练习
1.创建Student类

2.拥有以下属性: 姓名 性别 年龄 学校 班级

3.拥有以下方法

save(name) 其作用是将这个对象序列化到文件中

get_obj(name)	其作用是根据name从文件中反序列化为得到一个对象

分析save方法和get_obj 应该作为绑定给对象还是绑定给类

import json

class Student:

    school = "beijing"

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


    def save(self):
        dic = {"name":self.name,"sex":self.sex,
               "age":self.age,"classes":self.classes}
        with open(self.name,"wt",encoding="utf-8") as  f:
            json.dump(dic,f)

    @classmethod
    def get_obj(cls,name):
        with open(name,"rt",encoding="utf-8") as  f:
            dic = json.load(f)
        obj = cls(dic["name"],dic["sex"],dic["age"],dic["classes"])
        return obj




# stu1 = Student("阿尔法","man",20,"py5期")
# stu2 = Student("张三","woman",20,"py5期")


# stu1.save()
# stu2.save()

stu = Student.get_obj("阿尔法")
print(stu)
print(stu.name)

继承

1、什么是继承

​ 继承是一种关系,必须存两个对象才可能产生这种关系,在现实生活中的继承,儿子继承父亲的就叫继承

​ 被继承的称之为父,继承的一方称之为子

​ 在程序中继承指的是类与类之间的关系

2、为什么要使用继承

​ 在生活中,通过继承,子可以直接享受父提供的内容,例如财产

​ 在程序中,通过继承可以直接使用父类已有的代码

3、继承的使用

​ 缺定继承关系:在子类中类名后面加上括号,括号内加上父类名称即可

​ 在python中一个子类可以有多个父类,多个父类在括号中用逗号隔开

​ 子类可以使用父类中的属性

​ 子类也可以使用父类中的函数

​ 抽象:相同属性抽象出来放到共同的父类中1544784602725

class Father:
	pass
	
class GanDie:
	pass

class Son(Father,Gandie)# 继承的方法
	pass


class Person::
	def __init__(self, name age, sex)
		self.name = name
        self.age = age
        self.sex = sex
        
  	def eat(self):
        print("正在吃饭")
  	def student(self)
    	print("正在学习")
  
class Teacher(person):
    def terching(self):
        print("老师正在上课")
t1 = Teacher("blex",30,"woman")
t1.eat()
t1.study()

class Student(Person):
    pass
stu1 = Student
stu1.eat()
stu1.study()
#通过继承 避免了重复代码的编写
#通过抽象 避免了继承到一些不应该有的内容
#应该先抽象再继承
#在抽取过程中 可能会有一些跟业务需求无关的类,这是正常的 这些称之为公共父类
#公共父类的作用是存储多个自乐相同属性和技能

4、派生与覆盖

​ 什么是派生

​ 派生指的是 子类继承某个父类 并且拥有自己独特的属性或技能 该子类称之为派生类

​ 子类出现了与父类重复的名字,称之为覆盖

​ 子类出现与父类不同的名字称之为派生

class Person:
     def __init__(self, name, age, sex):
       	self.name = name
        self.age = age
        self.sex = sex
            
   	def sayHI(self):
        prtit('hello 我是%S 今年%s岁 性别:%s'%(self.name, self.age, self.sex))
       
class Test(Person): #该类不能称之为派生类,因为没有与任何独特的内容与父类完全一致
    pass

class Student(Person): #Student类就称之为派生类(有自己独特的属性与内容)
    def __init__(self,name,age,sex,number):
        self.name = name
        self.age = age
        self.sex = sex
        self.number = number

    # 上课
    def up_class(self):
        print("%s 正在上课.....")
    	

5、子类访问父类父方法

​ 子类访问父类有两种方法

​ 1、指名道姓的方法访问

​ 2、使用super的方法访问

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

    def sayHI(self):
        print("hello 我是%s 今年%s岁 性别:%s" % (self.name,self.age,self.sex))


class Student(Person):
    def __init__(self,name,age,sex,number):
        # self.name = name
        # self.age = age
        # self.sex = sex
        #上述代码与父类中完全相同
        Person.__init__(self,name,age,sex)
        self.number = number
    # 上课
    def up_class(self):
        print("%s 正在上课.....")

    def sayHI(self):
        # print("hello 我是%s 今年%s岁 性别:%s" % (self.name,self.age,self.sex))
        # 访问父类中的方法来简化代码
        # 指名道姓
        # Person.sayHI(self)
        super.sayHI()
        print("学号:",self.number)
stu1 = Student("阿三",20,"woman","9527")
# print(stu1)
# print(stu1.name,stu1.age,stu1.sex)
stu1.sayHI()

class Student(Person):
    def __init__(self,name,age,sex,number):
        super(Student,self).__init__(name,age,sex)
        self.number = number
       
 # 上课
    def up_class(self):
        print("%s 正在上课.....")

stu1 = Student("阿三",20,"woman","9527")
print(stu1)
print(stu1.name,stu1.age,stu1.sex)

        

7、存在继承关系后的属性查找顺序

​ 对象——》类——》父类——》父类的父类。。。。

​ 优先找对象,如果没有对象,则找类,如果类没有,会沿着继承关系一直找到最顶层的父类

​ 无论属性还是方法 查找方法都是一样的

​ 存在多个继承的话就按照继承的顺序,先继承谁就找谁

1544784901944

1544784820341

8、经典类与新式类

​ 所有直接继承或间接继承object的类 都是新式类

​ object 称之为 根类 意思是所有类 都源自于object类

​ 为什么这么设计

​ 例如:创建对象时,需要申请内存空间,创建新的名称空间将对象的属性放入名称空间,这一系列复杂的基础操作,都由object来完成

​ 简单地说object提供了一下常用的基础操作

​ 即所有类都属于新式类(在python3中)

​ 在python3中默认所有类都是新式类

​ 而python2中默认的是经典类(不会自动基础object)

​ ——bases——用于查看父类

​ 显示属性的查找顺序类别,继承查找属性就是按照mro来查找的

​ super访问父类内容时 按照mro列表属性查找

组合

什么叫组合

一个对象将另一个(或多个)对象作为属性

组合的目的

​ 降低冗余,降低耦合度

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


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

    def select_cursor(self):
        print("%s 正在选课...." % self.name)

class Teacher(Person):
    def __init__(self,name,sex,age,salary,level):
        super().__init__(name,sex,age)
        self.salary = salary
        self.level = level
    def set_score(self):
        print("%s 正在为学生打分..." % self.name)



# 上述代码通过继承减少了代码冗余
# # 需要给学生添加手机这个属性,(手机号码,运营商,归属地,可以打电话)
# stu = Student("李寻欢","女",20,"飞刀一班")
# print(stu.name)
# print(stu.phonenumber)


stu = Student("李寻欢","女",20,"飞刀一班")
stu.show_info()

​ 学生会增加各种各样的新的属性 比如手机 比如电脑, 这样Student中会增加大量的属性和方法
​ 后期的维护成本非常高
​ 这时就需要使用组合 来完成减少代码冗余

class Phone:
    def __init__(self,phonenumber,operator,address):
        self.phonenumber = phonenumber
        self.operator = operator
        self.address = address

    def call(self):
        print("%s 正在拨号" % self.phonenumber)


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


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

    def show_info(self):
        # print("name:%s sex:%s, age:%")
        print(self.__dict__)

    def select_cursor(self):
        print("%s 正在选课...." % self.name)

class Teacher(Person):
    def __init__(self,name,sex,age,salary,level):
        super().__init__(name,sex,age)
        self.salary = salary
        self.level = level
    def set_score(self):
        print("%s 正在为学生打分..." % self.name)


stu = Student("乔峰","男",38,"007")

# 学生买了一台手机 所以增加一个手机属性
p1 = Phone("1999999999","中国小米移动","上海浦东")

stu.phone = p1

# 学生要打电话
stu.phone.call()

# 通过将手机对象和学生对象进行组合  完成了需求,并且相比较继承而言
# 耦合度低  手机和学生在修改的时候 互不影响
# 同时也减少了代码冗余

# 继承是一种关系  什么是什么   学生是人类  老师是人类    (如果把手机相关内容插入到人类中,相当于学生是手机???)
# 组合是 对象之间的关系   学生对象拥有手机对象

封装

1、什么是封装

​ 生活中的封装类似工厂,将一堆产品装入纸箱,拿胶带封起来

​ 在程序中,封装指的是将内容隐藏起来,在面向对象中有什么内容可以被隐藏?就是属性和方法

2、为什么要封装

​ 1、提高安全性(例如:cup频率 缓冲区大小,电脑,手机汽车等)

​ 对于封装属性而言,是通过给范围和修改增加额外的逻辑判断来实现的

​ 2、封装是为了明确区分内部和外部

3、如何使用封装

​ 在属性或方法名称前 加上两个下划线,就可以将其设置为私有

​ 另外补充:python中权限只有两种,公开(谁都能访问)的和私有的(只有自己能访问)

​ 属性的封装,通过需要提供相应的设置器和访问器

4、什么时候用

5、封装的实现原理

6、封装的特点

​ 被隐藏的内容 在内部是可以直接访问的,外部无法访问

封装的方法:

class Student:
    def __init__(self,name,sex,age,idCard):
        self.name = name
        self.age = age
        self.sex = sex
        self.__idCard = idCard
    def say_hi(self):
        print("hello i am %s age is : %s sex is %s" %
              (self.name,self.age,self.sex))

    def test(self):
        print(self.__idCard)

    # 可以使用方法 将内部封装的内容返回出去
    def get_idCard(self):
        return self.__idCard

    # 如果直接返回的话 就没必要设置隐藏了 ,谁都可以通过调用方法来获取,我们再方法中加上自己的判断逻辑
    def get_idCard2(self,pwd): # 如果密码正确我就给你身份证号码
        if pwd == "1111":
            return self.__idCard
        else:
            print("滚 你没有资格知道我的身份证...")

    def set_idCard(self,pwd,new_idCard): # 如果密码正确就允许修改
        if pwd == "1111":
            self.__idCard = new_idCard
        else:
            print("滚 你没有资格修改我的身份证...")





stu = Student("步惊云","男",20,"3206661998445132")
stu.say_hi()

# print(stu.__idCard)  # 加上__也访问不了

# stu.test() # 但在内部是可以访问的

# idcard = stu.get_idCard()
# print(idcard)
# 即实现了隐藏  又提供了访问的接口 这就是封装的用法
# idcard = stu.get_idCard2("1111")
# print(idcard)


# 身份证填错了 要修改
# stu.__idCard = "123" # 这样是无法修改原来的身份证信息的 而是添加了新的属性
#
# print(stu.__idCard)
# print(stu.get_idCard2("1111"))

# 调用方法来修改隐藏的属性
stu.set_idCard("1111","xxxxxxxxxxxxxxxxxx")

print(stu.get_idCard2("1111"))


# 总结:对于被隐藏的属性  访问和修改都需要通过方法 get用于获取  set用于设置(也称之为设置器和访问器)
class A:

    def __f1(self):
        print("f1 run")

    def run_f1(self):
        self.__f1()

a = A()
a.run_f1()




# ATM 的取款功能
# 1.插入银行卡 2.输入密码 3.选择取款金额  4.取款

class ATM:
    def __insert_card(self):
        print("插入银行卡...")

    def __input_pwd(self):
        print("输入密码...")
    def __select_money(self):
        print("选择取款金额...")
    def withdraw(self):
        self.__insert_card()
        self.__input_pwd()
        self.__select_money()
        print("取款成功!....")

atm = ATM()

atm.withdraw() # 外部调用这个简单的接口 就能完成一系列复杂的操作

# atm.select_money()  #直接调用内部的方法 是没有意义的无法完成整个功能

# 当然用户按照流程一一调用也可以完成功能 但是太麻烦
# atm.insert_card()
# atm.input_pwd()
# atm.select_money()

# 封装方法 如何封装 给方法名字前面加上双下划线
# 封装方法的好处:
# 1.提高安全性
# 2.隔离复杂度  (将复杂的内容隔离到内部 外部只留下简单的接口 对于使用者 难度降低)

封装的实现原理

class Person:

    def __init__(self,name,sex,age,idCard):
        self.name = name
        self.sex = sex
        self.__age = age
        self.__idCard = idCard

    def get_idCard(self):
        return self.__idCard

    def __test(self):
        pass

    print("aaaaaaaaaa")


# p = Person("比尔盖茨","男",20,"322323232332332")
#
# # print(p.__idCard)
# p.__idCard = "XXXXXX"
# print(p.__idCard)
#
# print(p.get_idCard())
#
# print(p.__dict__)
# print(Person.__dict__)
#
#
# p.__xxxxxxxxxxxx = 1
#
# print(p.__dict__)


# 通过__dict__ 可以发现
# 1.私有的属性和方法名称 前自动加上了_类名 python就是通过这样的转换方式来实现封装
# 2.只有在类的内部的双下划线才会被自动转换,并且这个转换过程只执行一次,在类定义完成后 后续添加的双下划线开头的名称是不会自动转换的
# 3.父类中私有的方法 子类中无法使用


#在父类中定义的私有方法 能不能被子类所覆盖?

总结

封装指的是隐藏内部的实现细节,对外提供访问的接口

封装是隐藏,但是不是单纯的隐藏

能封装的内容:属性和方法

如何封装:名字前加__双下划线

对于被隐藏的属性访问和修改都需要通过方法 一般get用于获取,set用于设置(也称之为设置器和访问器)

property

当一些属性的值,不是固定的而是通过计算得来的时候,我们必须为这个属性增加方法才能完成计算

但是一旦使用方法后,该属性的范围的就成了方法的调用,很明显与其他属性访问方式不同,这个给使用者造成迷惑

所有需要将这个方法伪装成普通属性,这就用到了Property

property可以将方法伪装成属性,利用这个特点我们也可以将其使用到封装中

之前没有这个装饰器我们需要为私有的属性提供两个方法,但是这样一来访问私有属性的方法就发生了变化

这时候就可以使用property来进行伪装,使的访问访问私有属性与访问普通属性的方式一致

另外property还提供了setter(用于修改属性的值)和deleter(删除属性的值)

# BIM案例:
class Person:
    def __init__(self,name,weight,height):
        self.name = name
        self.weight = weight
        self.height = height
        # self.bmi = weight/(height*height)
    # def bmi(self):
    #     return self.weight / (self.height * self.height)
    @property
    def bmi(self):
        return self.weight / (self.height * self.height)

p = Person("尔晴",50,1.5)
# print(p.bmi)
# p.weight = 90
# print(p.bmi)

# 现在 虽然可以实现需求 但是我们把一个属性变成了一个行为 这是不合理的
# print(p.bmi())
# p.weight = 90
# print(p.bmi())

# 使用property装饰器 可以将一个方法伪装成一个属性
print(p.bmi)
p.height += 0.2
print(p.bmi)


class Student:
    def __init__(self,name,sex,idCard):
        self.name = name
        self.sex = sex
        self.__idCard = idCard

    def get_idCard(self):
        return self.__idCard

    def set_idCard(self,new_id):
        self.__idCard = new_id

    @property # 需要掌握
    def idCard(self):
        return self.__idCard


    @idCard.setter #了解的
    def idCard(self,new_id):
        self.__idCard = new_id

    @idCard.deleter # 了解的
    def idCard(self):
        print("身份证属性被删除了.....")
        del self.__idCard


stu = Student("尔康","男","323254554554")

# print(stu.get_idCard())

# stu.set_idCard("xxxx")

print(stu.get_idCard()) # 使用装饰器前
print(stu.name) # 普通属性的访问

print(stu.idCard) # 使用装饰器后

stu.idCard = "aaaaaaa" # 使用装饰器后的修改操作

print(stu.idCard)

del stu.idCard

print(stu.__dict__)
print(Student.__dict__)

多态

什么是多态

​ 多种状态 形态

​ 生活中具备多种形态的事物 水(水蒸气 冰 液态水)

​ 一种事物具备多种形态或状态 就称之为多态

​ 官方解释:不同对象 可以响应同一方法,并作出不同的行为,产生不同结果

如何实现多态?

​ 让几个不同类拥有相同的父类,这样一来他们就具备了形态的方法,每个子类要覆盖父类的方法

程序中的多态 例如动物

class Animal:
    def eat(self):
        print("动物在吃东西...")
    def sleep(self):
        print("动物在睡觉...")
    def drink(self):
        print("动物需要水.....")


class Person(Animal):
    def eat(self):
        print("人吃粮食...")

class Pig(Animal):
    def eat(self):
        print("猪吃饲料...")

class Dog(Animal):
    def eat(self):
        print("狗吃骨头...")


person = Person()
pig = Pig()
dog = Dog()

person.eat()
pig.eat()
dog.eat()

#假设你学习了C1驾照  意味着 所有C1类的汽车都能开  因为每种C1汽车的驾驶方式相同


# 当使用了多态之后 对象的使用者不需要关心这个对象具体的实现,只需要知道该对象属于哪个基类,就能直接使用它
# 如此扩展性变高了


class Phone:
    def call(self):
        print("手机就能打电话..")

    def send_msg(self):
        print("手机能发短信..")


class WindowsPhone(Phone):
    def call(self):
        print("拨号打电话..")

    def send_msg(self):
        print("输入号码发短信..")

class IPhone(Phone):
    def call(self):
        print("拨号打电话..")


    def send_msg(self):
        print("输入号码发短信..")




# 可以定义一个方法接受一个手机为参数 无论是是类型的手机 都可以被使用

def CALL(phone):
    phone.call()

wp = WindowsPhone()
ipx = IPhone()

CALL(wp)
CALL(ipx)


# 系统内置的方法有很多都体现了多态


print(len("abc"))
print(len([1,2,3,4,]))
print(len({"name":"123","sex":"man"}))

print("abc".__len__())
print([1,2,3,4,].__len__())
print({"name":"123","sex":"man"}.__len__())

print(len({1,23,4,5}))

多态之ABC模块

多态是多个对象拥有相同的方法,但是我们没有从严格要求说必须要提供这些方法,子类完全可以不提供这些方法

现在要做的就是,严格要求 子类必须实现父类声明的方法

import abc
# abstract class 是抽象类的缩写   抽象的意思是 不清晰 不具体 看不懂

#使用ABC模块来限制子类 的步骤
#1.为类中指定元类为abc.ABCMeta
#2.在相应的方法上加上abc.abstractmethod装饰器

class Animal(metaclass=abc.ABCMeta):

    @abc.abstractmethod
    def eat(self):
        pass

    @abc.abstractmethod
    def drink(self):
        pass


class Cat(Animal):
    def eat(self):
        print("猫爱吃鱼肉...")

    def drink(self):
        print("用舌头舔..")

class Dog(Animal):
    def eat(self):
        print("狗爱吃骨头...")
    def drink(self):
        print("用舌头舔..")

class Pig(Animal):
    def eat(self):
        print("猪 爱吃草...")

    def drink(self):
        print("用嘴吸的..")

p = Pig()
# p.eat()

c = Cat()
# c.eat()

# 多态的好处 完全不需要考虑得到的对象时声明类型 只要知道了其基类中的内容就能使用
def feeding(animal):
    animal.eat()
    animal.drink()

鸭子类型

python推崇简单的编程方法

鸭子类型 如果一个对象叫声象鸭子 走路也象鸭子 那就把它当成鸭子

对应到代码中就是:只要你的行为一样 那就把你当成同一个类型来看待

class Duck:

    def bark(self):
        print("鸭子嘎嘎叫...")

    def run(self):
        print("摇摇晃晃走....")

class Chicken:
    def bark(self):
        print("鸡咯咯叫...")

    def run(self):
        print("摇摇晃晃走....")

def test(obj):
    obj.bark()
    obj.run()
duck = Duck()
c = Chicken()

test(duck)
test(c)

# 如果你足够自觉 你可以不使用abc模块 也不需要基类 自觉地将方法名字都写成一样 同样可以实现多态
# 这种方式称之为鸭子类型

isinstance和issubclass

class Person:
	pass
class Student:
	pass

stu = Student():

print(type(1) == type(1))  # 判断两对象是不是同一个类型

print(isinstance(stu,Student))  # 判断stu对象是不是S图dent类的实例
print(issubclass(Student,Person))  # 判断一个类是不是另一个类的子类(所有类都是object的子类或子子类)


反射

反射 其实说的就是反省

简单的说 就是对象具备一种修正错误的能力

hasattr 是否存在某个属性

getattr 获取某个属性的值

setattr 设置摸个属性的值

delattr 删除某个属性的值

这几方法有一个共同点 都是通过字符串来操作属性

可以理解为,通过字符串来操作属性,就叫反省

class Student(object):
def __init__(self,name,sex,age):
	self.name = name
	self.sex = sex 
	self.age = age
	def study(self):
        print("学生正在学习")
stu = Student("egon","woman")
#当你获取到一个对象时,但是并不清楚这个对象内部细节是,就需要使用反射了
def test(obj):
    if hasattr(obj,"name")
    	print(getattr(obj,"name","没有name属性"))
test(stu)


setattr(stu,"school","beijing")
delattr(stu,"shchool")
print(getattr(stu,"school",,"没有学校属性"))
delattr(stu,"age")
print(stu.age)

        

总结:

​ 如果在编码期间 就能明确知道我要访问的属性,就没有必要使用反射

​ 如果在编码期间,无法明确知道我要访问的属性,就应该使用反射

class Student:
    def study(self):
        print("学习中....")


stu = Student()

res = getattr(stu,"study",None)

print(res)

def eat(self):
    print("正在吃饭...")

# 可以通过反射的方式为对象增加一个方法 但是注意 这样增加的方法就是一个普通函数 不会自动传值
setattr(stu,"eat",eat)

print(getattr(stu,"eat",None))

练习:

# 需要编写一个CMD工具  这个工具可以支持两个命令 dir ,tasklist

class CMD:

    def dir(self):
        print("列出当前文件夹目录....")

    def tasklist(self):
        print("查看任务列表.....")


cmd = CMD()


res = input("请输入指令:").strip()

if hasattr(cmd,res):
    func = getattr(cmd,res)
    print(func)
    func()
else:
    print("输入的指令不正确....")

类中的特殊的内置函数

前后带双下滑线的都是特殊的内置函数 会在莫些时机自动执行,一般不情况下我们不应该直接调用

——str——

当我们需要自定义打印显示内容时,就需要实现——str——方法

该方法必须返回一个字符串,返回的是什么,打印出来就是什么

class Test:
    def __init__(self,name):
        self.name = name
    def __str__(self):
        print("str run....")
        return self.name
t = Test("安米")

print(int(1).__str__())
# print([1,2,3,5])
# print(t)

# 在讲一个对象转换字符串时  本质就是在调用这个对象 __str__方法
print(str(t))

——del——

当对象被从内存中删除时,会自动执行

另一种情况时 程序员手动删除了这个对象也会自动执行

什么时候使用它

​ 在Python中有自动内存管理机制,所有python自己创建的数据不需要我们做任何操作

​ 但是有一种情况,我们使用python打开了一个不属于python管理的数据时

​ 比如打开一个文件,这个文件一定是操作系统在打开的,会占用系统内存,而python解释器无法操作系统内存的

所以当你python解释器运行结束后,文件依然处于打开状态,这时候就需要使用——del——来关闭操作文件

​ 简单的说,当程序运行结束时,需要做一些清理操作 就使用——del——

​ ——del——也称之为析构函数:分析构造,并拆除对象

class Student:

    def __del__(self):
        print("对象被删除了....")


stu = Student()

# 手动删除 立即执行__del__
del stu

import time

time.sleep(5)
class TextFile:

    def __init__(self,filepath,mode="rt",encoding="utf-8"):
        self.file = open(filepath,mode=mode,encoding=encoding)

    def read(self):
        return self.file.read()

    def write(self,text):
        self.file.write(text)

    # 该方法其实就是一个通知性质 仅仅是告诉程序员 对象即将被删除
    def __del__(self):
        # 在这里关闭系统的文件 妥妥的
        self.file.close()


tf = TextFile("2.今日内容.txt")

print(tf.read())

# tf.file.close() 不需要手动关闭了  在对象删除时会自动关闭

tf.read()

exec

execute的缩写

表示执行的意思

其作用,是帮你解析python代码,并且将得到的名称存储到指定的名称空间,解释器内部也是调用它来执行代码的

exec需要三个参数

1、需要一个字符串对象,表示需要执行的python语句

2、是一个字典,表示全局名称空间

3、也是一个字典,表示局部名称空间

# 如果同时制定了 全局和局部 则 会字符串中包含名称 解析后存到局部中
# print(globalsdic)
print(localsdic)
localsdic["func1"]()





# # 如果只传了一个传参数 则 将字符串中包含名称 解析后存到全局中
exec("""
aaaaaaaaaaaaaaaaaaaa = 1
bbbbbbbbbbbbbbbbbbbbbbbbbbbb = 2

""",localsdic)

元类

元类是指 用于产生类的类,type就是元类

所有的自定义都是通过type实例化得来

创建模块的过程

1、创建一个空的名称空间

2、执行内部的代码

3、将得到的名字放到名称空间中

# class也是一个对象
class Student(object):

    school = "北京大学!"

    def study(self):
        print("学习中...")



# 使用type可以发现 类其实是type类型的实例(对象)
print(type(Student))

# 我们可以自己调用type来实例化产生一个类



# myclass 包含的代码
code = """
name = "张三"
age = 18
def hello(self):
    print("hello %s" % self.name)
"""



#类的名字
class_name = "MyClass"
#类的的父类们
base_classes = (object,)
#类的名称空间
namespace = {}

exec(code,{},namespace)


res = type(class_name,base_classes,namespace)

print(Student)
print(res.name)
print(res.age)
print(res.hello)



# 1.类是由type实例化产生的
# 2.我们可以使用type来产生一个类
# 3.一个类是由 类名字 类的父类元祖 类的名称空间 三个部分组成


class Test(object): #Test = type("Test",(object,),{})
    pass

——call——

调用的意思

在对象呗调用时执行(函数,类)

自定义元类的目的

1、可以通过——call——来控制对象的创建过程

2、可用控制类的创建过程

# 自定义一个元类 元类也是一个类   但是需要继承type
class MyMeta(type):

    # self 表示要创建对象的那个类(Person)  *args是调用Person类时传入的参数
    def __call__(self, *args, **kwargs):

        print("MyMte中的 call run'")
        print(self,*args,**kwargs)

        # 下面的三步是固定写法 一个模板 只要你需要控制对象的创建过程 就应该先把模板写出来

        # 1.创建空对象
        obj = object.__new__(self)
        # 2.调用初始化方法
        self.__init__(obj,*args,**kwargs)
        # self.__init__(obj)
        # 3.得到一个完整的对象
        return obj



# 修改Person类的元类为MyMeta
class Person(metaclass=MyMeta):

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

    def __call__(self, *args, **kwargs):
        print("call run...")


#调用Person这个对象时 执行的是 Person的类(type)中__call__ 方法
p = Person("张三疯",80)

print(p)
# 当调用对象时 会执行该对象所属类中的__call__方法
# p()

print(p.name)
print(p.age)

——init——和——doc——

要控制类的创建过程,只要找到类所属中的——init——即可

——doc——代表的是文档注释

 def __init__(self,class_name,bases,namespace):
        print("============================")
        #print(self.__dict__)
        # 我要控制 类的名字  必须 是大写开头
        if not class_name.istitle():
            print("类名 必须大写开头...... ")
            # 该代码是主动抛出异常
            raise TypeError("类名 必须大写开头...... ")
        #要空类的创建 必须包含__doc__这个属性
        if not self.__doc__:
            raise TypeError("类中必须有文档注释.....")

        pass

class Student(metaclass=MyMeta):   # Student = MyMeta("Student",(object,),{})
    """
        这是文档注释  可以通过__doc__来获取
        这是一个学生类
    """

    # 在类的__init__中可以控制该类对象的创建过程
    def __init__(self,name):
        print("-----------------------")
        print(self.__dict__)
        self.name = name

print(Student.__doc__)

元类总结:

元类是用于创建类的类

学习元类是为了能控制类的创建过程以及类实例化对象的过程

一、控制类创造的过程

​ 1、创建一个元类(需要继承type)、

​ 2、覆盖——init——方法,该方法会将新建的类对象,类名、父类名,名称空间都传进来,可以利用这些信息再做处理

​ 3、对于需要 被控制的类,需要指定metaclass为上面的元类

二、控制类实例化对象的过程

​ 1、创建一个元类(需要继承type)

​ 2、覆盖——call——方法会将正在实例化对象的类,调用类是传入的参数都传进来

​ 3、在——call——方法中,必须先编写模板代码

​ 3.1、创建空对象

​ 3.2、调用类的——init——方法来初始化这个空对象

​ 3.3返回该对象

​ 4、加入你需要控制的逻辑

类的三个组成部分

类名,父类们,名称空间

元类——》实例化产生——》类——》实例化产生——》对象

单例模式

一种设计模式(套路)

单个实例

​ 一个类如果只有一个实例,那么该类就称之为单例

class MyMeta(type):

    obj = None
    def __call__(self, *args, **kwargs):
        if not MyMeta.obj:
            obj = object.__new__(self)
            self.__init__(obj,*args,**kwargs)
            MyMeta.obj = obj
        return MyMeta.obj


#打印机类
class  Printer(metaclass=MyMeta):
    """
    这是一个单例类 请不要直接实例化 使用get方法来获取实例
    """

    obj = None
    def __init__(self,name,brand,type):
        self.name = name
        self.brand = brand
        self.type = type

    def printing(self,text):
        print("正在打印 %s"  % text)

    # 通过该方法来获取对象 可以保证只有一个对象
    # 但是这还不够 因为 还是可以通过调用类产生新对象
    # 就应该使用元类 来控制实例化的过程 __call__
    # 在__call__ 中编写代码 保证每次调用call 都返回同一个实例 即可

    @classmethod
    def get_printer(cls):
        if not cls.obj:
            obj = cls("ES005","爱普生","彩色打印机")
            cls.obj = obj
            print("创建了新的对象")

        return cls.obj

# 以下三个对象 的数据完全相同 但是却 占用三分内存空间
# p1 = Printer("ES005","爱普生","彩色打印机")
# p2 = Printer("ES005","爱普生","彩色打印机")
# p3 = Printer("ES005","爱普生","彩色打印机")

# 现在要处理问题就是  如何能够限制该类 只能实例化一个对象

p = Printer.get_printer()
print(p)

p = Printer.get_printer()
print(p)

p = Printer.get_printer()
print(p)

p = Printer.get_printer()
print(p)
p1 = Printer("ES005","爱普生","彩色打印机")
p2 = Printer("ES005","爱普生","彩色打印机")

print(p1)
print(p2)
# print(p1,p2,p3)
posted @ 2018-12-20 14:34  shengkengliu  阅读(162)  评论(0编辑  收藏  举报