面向对象编程

面向对象基础

面向对象编程介绍

'''
面向过程的编程思想
    核心过程二字,过程指的是解决问题的步骤,即先干什么\再干什么\后干什么
    基于该思想编写程序就好比在设计一条流水线,是一种机械式的思维方式

    优点:复杂的问题流程化\进而简单化
    缺点:可扩展性差

面向对象的编程思想
    核心是对象二字,对象是特征与技能的结合体
    基于该思想编写程序(脑子始终想的是对象二字)就好比在创造一个世界,世界是由一个个对象组成,在上帝眼里任何存在的事物都是对象,任何不存在的事物也都可以造出来,是一种上帝式的思维方式

    优点: 可扩展性强
    缺点: 编程的复杂度要高与面向过程


类:
    对象是特征与技能的结合体,而类则是一系列对象相同的特征与技能的结合体
    强调:
        1.对象是具体存在的事物,而类则一个抽象的概念
        2.站在不同的角度总结出的类与对象是不同的

在现实世界中:先有一个个具体存在的对象,然后随着人类文明的发展才总结出类的概念
在程序中: 先定义类,后调用类来产生对象

站在老男孩选课系统的角度
总结出现实世界中的老男孩学生对象:
    学生对象1
        特征
            school='oldboy'
            name='李铁蛋'
            age=18
            sex='male'
        技能
            选课

    学生对象2
        特征
            school='oldboy'
            name='赵钢弹'
            age=38
            sex='female'
        技能
            选课

    学生对象3
        特征
            school='oldboy'
            name='刘银弹'
            age=28
            sex='male'
        技能
            选课

    老师对象1
        特征
            school='oldboy'
            name='Egon'
            age=18
            sex='male'
            level=10
        技能
            打分

总结现实世界中的老男孩学生类
    相同的特征
        school='oldboy'
    相同的技能
        选课

在程序中....
'''

类的使用

'''
站在老男孩选课系统的角度
总结出现实世界中的老男孩学生对象:
    学生对象1
        特征
            school='oldboy'
            name='李铁蛋'
            age=18
            sex='male'
        技能
            选课

    学生对象2
        特征
            school='oldboy'
            name='赵钢弹'
            age=38
            sex='female'
        技能
            选课

    学生对象3
        特征
            school='oldboy'
            name='刘银弹'
            age=28
            sex='male'
        技能
            选课

总结现实世界中的老男孩学生类
    相同的特征
        school='oldboy'
    相同的技能
        选课
'''
# 在程序中
# 1. 先定义类
class OldboyStudent:
    #相同的特征
    school = 'oldboy'
    #相同的技能
    def choose_course(self):
        print('choosing course')
    # print('===>>>')
    
# 类是一系列对象相同的特征(变量)与技能(函数)的结合体,即类体中最常见的就是变量与函数的定义
# 但其实类体中是可以存在任意python代码的
# 类体代码会在类定义阶段立即执行,会产生一个类名称空间,用来将类体代码执行过程中产生的名字都丢进去,查看方式如下
print(OldboyStudent.__dict__) # 查看类的名称空间
print(OldboyStudent.school) # print(OldboyStudent.__dict__['school'])
print(OldboyStudent.choose_course)
OldboyStudent.school='OLDBOY' # OldboyStudent.__dict__['school']='OLDBOY'
OldboyStudent.country='China' # OldboyStudent.__dict__['country']='China'
del OldboyStudent.country # del OldboyStudent.__dict__['country']
print(OldboyStudent.__dict__)

OldboyStudent.choose_course(123123)
# 总结:
# 1. 类本质就是一个名称空间,或者说是一个用来存放变量与函数的容器
# 2. 类的用途之一就是当做名称空间从其内部取出名字来使用
# 3. 类的用途之二是调用类来产生对象


#2. 后调用类来产生对象

对象的使用

'''
学生对象1
        特征
            school='oldboy'
            name='李铁蛋'
            age=18
            sex='male'
        技能
            选课

    学生对象2
        特征
            school='oldboy'
            name='赵钢弹'
            age=38
            sex='female'
        技能
            选课

    学生对象3
        特征
            school='oldboy'
            name='刘银弹'
            age=28
            sex='male'
        技能
            选课
'''

class OldboyStudent:
    #相同的特征
    school = 'oldboy'
    #相同的技能
    def choose_course(self):
        print('choosing course')

# 2. 后调用类来产生对象:
# 调用类的过程称之为类的实例化,调用类的返回值称之为类的一个对象/实例

# 调用类发生了?
# 1. 会产生一个对象,然后返回
stu1=OldboyStudent()
stu2=OldboyStudent()
stu3=OldboyStudent()
# print(stu1.__dict__)
# print(stu2.__dict__)
# print(stu3.__dict__)

# 为对象定制自己独有的特征
'''
# name='李铁蛋'
# age=18
# sex='male'
stu1.name='李铁蛋'
stu1.age=18
stu1.sex='male'
print(stu1.__dict__)

# name='赵钢弹'
# age=38
# sex='female'
stu2.name='赵钢弹'
stu2.age=38
stu2.sex='female'
print(stu2.__dict__)

# name='刘银弹'
# age=28
# sex='male'
stu3.name='刘银弹'
stu3.age=28
stu3.sex='male'
print(stu3.__dict__)
'''

# 为对象定制自己独有的特征,简化方案一:
'''
def init(obj,name,age,sex):
    obj.name = name
    obj.age = age
    obj.sex = sex

# stu1.name='李铁蛋'
# stu1.age=18
# stu1.sex='male'
init(stu1,'李铁蛋',18,'male')

# stu2.name='赵钢弹'
# stu2.age=38
# stu2.sex='female'
init(stu2,'赵钢弹',38,'female')

# stu3.name='刘银弹'
# stu3.age=28
# stu3.sex='male'
init(stu3,'刘银弹',28,'male')

print(stu1.__dict__)
print(stu2.__dict__)
print(stu3.__dict__)
'''

# 为对象定制自己独有的特征,简化方案二:
class OldboyStudent:
    #相同的特征
    school = 'oldboy'
    
    #           stu1,'李铁蛋',18,'male'
    def __init__(obj, name, age, sex):
        obj.name = name #stu1.name='李铁蛋'
        obj.age = age   #stu1.age=18
        obj.sex = sex   #stu1.sex='male'

    #相同的技能
    def choose_course(self):
        print('choosing course')

# 调用类发生了
# 1. 先产生一个空对象stu1,然后返回
# 2. 触发类中函数__init__的执行,将对象连同调用类括号内指定的参数一同传入__init__(stu1,'李铁蛋',18,'male')

stu1=OldboyStudent('李铁蛋',18,'male') #__init__(stu1,'李铁蛋',18,'male')
stu2=OldboyStudent('赵钢弹',38,'female') #__init__(stu2,'赵钢弹',38,'female')
stu3=OldboyStudent('刘银弹',28,'male') #__init__(stu3,'刘银弹',28,'male')

# print(stu1.__dict__)
# print(stu2.__dict__)
# print(stu3.__dict__)
# 总结__init__的功能: 是在实例化时就为对象初始自己独有的特征
# 注意:不能有返回值

# stu1.xxx=111
# print(stu1.__dict__)

属性查找

xxx=33333
class OldboyStudent:
    school = 'oldboy'
    # xxx=2222
    yyy=333
    count=0

    def __init__(obj, name, age, sex):
        # print(OldboyStudent.yyy)
        # print(obj.yyy)

        OldboyStudent.count+=1 #实现对对象的引用计数
        obj.name = name #stu1.name='李铁蛋'
        obj.age = age   #stu1.age=18
        obj.sex = sex   #stu1.sex='male'

    def choose_course(self):
        print('choosing course')

# 1. 属性查找顺序
stu1=OldboyStudent('李铁蛋',18,'male')
print(stu1.__dict__)
print(OldboyStudent.__dict__)
stu1.xxx=111
print(stu1.__dict__)
# 先从对象自己的名称空间找,没有则去所属的类中找
print(stu1.xxx)

# 2.
stu1=OldboyStudent('李铁蛋',18,'male')
stu2=OldboyStudent('赵钢弹',38,'female')
stu3=OldboyStudent('刘银弹',28,'male')
# print(stu1.count)
# print(stu2.count)
# print(stu3.count)
# print(OldboyStudent.count)

# 类中定义的变量是所有对象共享的,对象可以来用,类也可以来使用,类一旦改变自己的数据属性的值,所有的对象都能感知到
# print(id(stu1.school))
# print(id(stu2.school))
# print(id(stu3.school))
# print(id(OldboyStudent.school))

stu1.school=123  #只改变对象中的变量不会影响类中的变量值
# OldboyStudent.school='OLDBOY' #改变类会影响到所有对象中的值
# print(stu1.__dict__)
# print(stu2.__dict__)
# print(stu3.__dict__)
# print(OldboyStudent.__dict__)
#
# print(OldboyStudent.school)
# print(stu1.school)
# print(stu2.school)
# print(stu3.school)

# print(id(stu1.choose_course))
# print(id(stu2.choose_course))
# print(id(stu3.choose_course))

绑定方法

# 类中定义的变量是类的数据属性,类可以用,对象也可以用,大家都指向同一个内存地址,类变量值一旦改变所有对象都跟着变

# 类中定义的函数是类的函数属性,类可以用,类来调用就是一个普通的函数,但其实类中定义的函数是给对象用的,而且是绑定给对象用的
# 绑定???
class OldboyStudent:
    school = 'oldboy'

    def __init__(self, name, age, sex):
        self.name = name #stu1.name='李铁蛋'
        self.age = age   #stu1.age=18
        self.sex = sex   #stu1.sex='male'

    def choose_course(self,x): #self=stu1
        print('%s choosing course' %self.name)

stu1=OldboyStudent('李铁蛋',18,'male')
stu2=OldboyStudent('赵钢弹',38,'female')
stu3=OldboyStudent('刘银弹',28,'male')

# 1. 类的函数: 该传几个参数就传几个
# print(OldboyStudent.__init__)
# print(OldboyStudent.choose_course)
# OldboyStudent.choose_course(stu1)

# 2. 绑定方法,指向类的函数: 特殊之处是绑定给谁就应该由谁来调用,谁来调用就会将谁当做第一个参数自动传入
# print(stu1.choose_course)
# print(stu2.choose_course)
# print(stu3.choose_course)

# stu1.choose_course(123) # OldboyStudent.choose_course(stu1,123)
# stu2.choose_course(123)
# stu3.choose_course(123)
# stu1.choose_course() #这样会报错,因为没有给x传参

一切皆对象

# 在python3中统一了类与类型的概念
class Foo:
    pass
# print(Foo)
# obj=Foo()
# print(type(obj))

# print(int)
# age=10 #age=int(10)
# print(type(age))

l1=[1,2,3] #l1=list([1,2,3])
# print(type(l1))
# l1.append(4)
# print(l1)

l1.append(4) # 相当于 list.append(l1,4)
print(l1)

l2=['a','b','c'] # l2=list(['a','b','c'])
l2.append('d')

三大特性之一:继承

继承介绍

'''
1 什么是继承
    继承一种新建类的方式,新建的类称之为子类/派生类,被继承的类称之为父类\基类\超类

    python中继承的特点:
        1. 子类可以遗传/重用父类的属性
        2. python中一个子类可以同时继承多个父类
        3. 在继承背景下去说,python中的类分为两种:新式类,经典类
            新式类: 但凡继承了object的类Foo,以及该类的子类...都是新式类
                在python3中一个类即便是没有显式地继承任何类,默认就会继承object
                即python3中所有的类都是新式类

            经典类:没有继承object的类,以及该类的子类...都是经典类
                在python2中才区分新式类与经典类,
                在python2中一个类如果没有显式地继承任何类,也不会继承object

2 为何要用继承
    减少类与类之间代码冗余

3 如何用继承
'''
class Parent1(object): # 父类
    pass

class Parent2(object): # 父类
    pass

class Sub1(Parent1): # 子类,继承父类
    pass

class Sub2(Parent1,Parent2): # 子类,多继承父类
    pass

print(Parent1.__bases__) #(<class 'object'>,)
print(Parent2.__bases__) #(<class 'object'>,)
print(Sub1.__bases__) #(<class '__main__.Parent1'>,)
print(Sub2.__bases__) #(<class '__main__.Parent1'>, <class '__main__.Parent2'>)


class Parent1(object):
    xxx=333
class Sub1(Parent1):
    # xxx=222  # 333
    pass
obj=Sub1()
# obj.xxx=111  # 222
print(obj.xxx)  # 111

# 问题:
# 1 子类如何重用父类的属性
# 2 在继承背景下,属性查找的优先级
# 3 新式类与经典类在属性查找上的区别

利用继承来解决类与类之间代码冗余问题

class OldboyPeople:  # 父类(=子类1+子类2)
    school = 'Oldboy'
    def __init__(self,name,age,sex):
        self.name = name
        self.age = age
        self.sex = sex

class OldboyStudent(OldboyPeople):  # 子类1
    # def __init__(self, name, age, sex, score=0):
    #     self.name = name
    #     self.age = age
    #     self.sex = sex
    #     self.score = score
    def choose_course(self):
        print('%s choosing course' % self.name)

class OldboyTeacher(OldboyPeople):  # 子类2
    # def __init__(self,name,age,sex,level):
    #     self.name=name
    #     self.age=age
    #     self.sex=sex
    #     self.level=level
    def score(self,stu,num):
        stu.score=num

stu1=OldboyStudent('刘二蛋',38,'male')
print(stu1.__dict__)

tea1=OldboyTeacher('egon',18,'male')
print(tea1.__dict__)

在子类派生的新方法中重用父类功能的方式一

# 在子类派生出的新方法中重用父类功能的方式一:
# 指名道姓地引用某一个类中的函数
# 总结:
# 1. 与继承无关
# 2. 访问是类的函数,没有自动传值的效果
class OldboyPeople:
    school = 'Oldboy'
    def __init__(self,name,age,sex):
        self.name = name
        self.age = age
        self.sex = sex

class OldboyStudent(OldboyPeople):
    def __init__(self, name, age, sex, score=0):
        OldboyPeople.__init__(self,name,age,sex)
        self.score = score
    def choose_course(self):
        print('%s choosing course' % self.name)

class OldboyTeacher(OldboyPeople):
    def __init__(self,name,age,sex,level):
        OldboyPeople.__init__(self,name,age,sex)
        self.level=level
    def score(self,stu,num):
        stu.score=num

stu1=OldboyStudent('刘二蛋',38,'male')
print(stu1.__dict__)

tea1=OldboyTeacher('egon',18,'male',10)
print(tea1.__dict__)

在单继承背景下的属性查找


# 在单继承背景下属性的查找优先级:对象->对象的类->父类->父类.....
class Foo:
    # xxx=444
    pass
class Bar1(Foo):
    # xxx=333
    pass
class Bar2(Bar1):
    # xxx=222
    pass
obj=Bar2()
# obj.xxx=111
print(obj.xxx)

class Foo:
    def f1(self):
        print('Foo.f1')
    def f2(self):
        print('Foo.f2')
        self.f1() #obj.f1()

class Bar(Foo):
    def f1(self):
        print('Bar.f1')

obj=Bar()
obj.f2()
'''
Foo.f2
Bar.f1
'''

在多继承背景下的属性查找

# 在多继承背景下属性的查找优先级:
# 如果一个子类继承多个分支(多个分支没有共同继承一个非object的类)
# 此时属性的查找优先级是:对象->对象的类->按照从左往右的顺序一个分支一个分支的找下去

# 第四层:
class G:
    # x = 'G'
    pass

# 第三层
class E(G):
    # x = 'E'
    pass
class F:
    # x = 'F'
    pass

# 第二层
class B(E):
    # x = 'B'
    pass
class C(F):
    # x = 'C'
    pass
class D:
    # x = 'D'
    pass

# 第一层
class A(B, C, D):
    # x = 'A'
    pass

obj = A()
# obj.x = 111
print(obj.x)

# 菱形继承问题:
# 新式类 : 广度优先查找,从左往右一个分支一个分支的查找,在最后一个分支才去查找顶级类
# 经典类 : 深度优先查找,从左往右一个分支一个分支的查找,在第一个分支就查找顶级类
# 第四层:
class G(object):
    # x = 'G'
    pass

# 第三层
class E(G):
    # x = 'E'
    pass
class F(G):
    # x = 'F'
    pass

# 第二层
class B(E):
    # x = 'B'
    pass
class C(F):
    # x = 'C'
    pass
class D(G):
    # x = 'D'
    pass

# 第一层
class A(B, C, D):
    # x = 'A'
    pass

obj=A()
# obj.x=111
print(obj.x)
#新式类(广度优先): obj->A->B->E->C-F->D->G->object
#经典类(深度优先): obj->A->B->E->G->C-F->D

# python专门为新式类内置了一个mro的方法,用来查看c3算法的计算结果,结果是??
print(A.mro())

在子类派生的新方法中重用父类功能的方式二

# 在子类派生出的新方法中重用父类功能的方式二:super()必须在类中用
# 在python2中:super(自己的类名,自己的对象)
# 在python3中:super()
# 调用该函数会得到一个特殊的对象,该对象专门用来访问父类中的属性,!!!完全参照mro列表!!!!
# 总结:
# 1. 严格依赖继承的mro列表
# 2. 访问是绑定方法,有自动传值的效果

class OldboyPeople:
    school = 'Oldboy'
    def __init__(self,name,age,sex):
        self.name = name
        self.age = age
        self.sex = sex
class OldboyStudent(OldboyPeople):
    def __init__(self, name, age, sex, score=0):
        super(OldboyStudent,self).__init__(name,age,sex)
        self.score = score
    def choose_course(self):
        print('%s choosing course' % self.name)
class OldboyTeacher(OldboyPeople):
    def __init__(self,name,age,sex,level):
        super().__init__(name,age,sex)
        self.level=level
    def score(self,stu,num):
        stu.score=num
stu1=OldboyStudent('刘二蛋',38,'male')
print(stu1.__dict__)
tea1=OldboyTeacher('egon',18,'male',10)
print(tea1.__dict__)


class A:
    def f1(self):
        print('A.f1')
        super().f2()
class B:
    def f2(self):
        print('B.f2')
class C(A,B):
    def f2(self):
        print('C.f2')
obj=C()
print(C.mro())
obj.f1()  # 结果要根据mro中的值来判断
'''
[<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>]
A.f1
B.f2
'''

继承总结

    1. 什么是继承
        继承是一种新建类的方式,新建的类称之为子类/派生类,被继承的类称之为父类/基类/超类

        继承有3个特点:
            1. 子类可以遗传/重用父类的属性(解决类与类之间代码冗余的问题)
            2. 在python中一个子类可以同时继承多个父类
            3. 在继承的背景下,类分为两种:新式类,经典类
                新式类:但凡继承object类的子类,以及该子类的子子孙孙都是新式类
                    在python3中一个类即便是没有显式地继承任何类,默认就继承object类,
                    即在python3中所有类都是新式类
                经典类:没有继承object类的子类,以及该子类的子子孙孙都是经典类
                    强调:只有在python2中才有经典类
                    在python2中如果一个类没有显式地继承任何类,并不会自动继承object类

    2. 为何要用继承:减少类与类之间的代码冗余

    3. 如何继承
        class Foo:
            pass
        class Bar(Foo):
            pass

        #obj=Bar()
        #obj.x

        Bar.x

    4. 属性查找
        在没有出现菱形继承的情况下,属性的查找是按照从左往右的顺序一个分支一个分支的找下去
        在出现菱形继承(一个子类继承的多个分支最终汇聚到一个非object类)的情况下,
            新式类:广度优先查找,按照从左往右的顺序一个分支一个分支的找下去,在最后一个分支才去查找顶级类
            经典类:深度优先查找,按照从左往右的顺序一个分支一个分支的找下去,在第一个分支就查找顶级类


    5. 派生
        在子类中定义自己的属性,如果与父类的属性重名,那以自己的为准.

        在子类派生的新方法中重用父类的功能:
            方式一:指名道姓地调用某一个类的函数
                特点:
                    1. 与继承无关
                    2. 没有自动传值的效果

            方式二:super()得到一个特殊的对象,该对象专门用来引用父类的属性
                特点:
                    1. 严格依赖继承,完全参照mro
                    2. 有自动传值的效果

组合

'''
1. 什么是组合
    组合指的是某一个对象拥有一个属性,该属性的值是另外一个类的对象
    class Foo:
        pass

    class Bar:
        pass

    obj=Foo()
    obj.attr=Bar()

    obj.xxx
    obj.attr.yyy

2. 为何要用组合
    通过为某一个对象添加属性(属性的值是另外一个类的对象)的方式,可以间接地将两个类关联/整合/组合到一起
    从而减少类与类之间代码冗余
    class Foo1:
        pass
    class Foo2:
        pass
    class Foo3:
        pass

    class Bar:
        pass

    obj_from_bar=Bar()

    obj1=Foo1()
    obj2=Foo2()
    obj3=Foo3()

    obj1.attr1=obj_from_bar
    obj2.attr2=obj_from_bar
    obj3.attr3=obj_from_bar

3. 如何用组合
class OldboyPeople:
    school = 'Oldboy'

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

class OldboyStudent(OldboyPeople):
    def __init__(self, name, age, sex,score=0):
        OldboyPeople.__init__(self,name,age,sex)
        self.score = score

    def choose_course(self):
        print('%s choosing course' % self.name)

class OldboyTeacher(OldboyPeople):
    def __init__(self,name,age,sex,level):
        OldboyPeople.__init__(self,name,age,sex)
        self.level=level

    def score(self,stu,num):
        stu.score=num

class Course:
    def __init__(self,c_name,c_price,c_period):
        self.c_name = c_name
        self.c_price = c_price
        self.c_period = c_period

    def tell_info(self):
        print('<课程名:%s 价钱:%s 周期:%s>' %(self.c_name,self.c_price,self.c_period))

# 创建课程对象
python=Course('python全栈开发',1900,'5mons')
linux=Course('linux架构师',900,'3mons')

stu1=OldboyStudent('刘二蛋',38,'male')
stu1.course=python
# print(stu1.__dict__)
stu1.course.tell_info()

tea1=OldboyTeacher('egon',18,'male',10)
tea1.course=python
# print(tea1.__dict__)
tea1.course.tell_info()
'''

class OldboyPeople:
    school = 'Oldboy'

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

class OldboyStudent(OldboyPeople):
    def __init__(self, name, age, sex,score=0):
        OldboyPeople.__init__(self,name,age,sex)
        self.score = score
        self.courses=[]

    def choose_course(self):
        print('%s choosing course' % self.name)

    def tell_all_course(self):
        print(('学生[%s]选修的课程如下' %self.name).center(50,'='))
        for obj in self.courses:
            obj.tell_info()
        print('='*60)

class OldboyTeacher(OldboyPeople):
    def __init__(self,name,age,sex,level):
        OldboyPeople.__init__(self,name,age,sex)
        self.level=level
        self.courses=[]

    def score(self,stu,num):
        stu.score=num

    def tell_all_course(self):
        print(('老师[%s]教授的课程如下' %self.name).center(50,'*'))
        for obj in self.courses:
            obj.tell_info()
        print('*'*70)

class Course:
    def __init__(self,c_name,c_price,c_period):
        self.c_name = c_name
        self.c_price = c_price
        self.c_period = c_period

    def tell_info(self):
        print('<课程名:%s 价钱:%s 周期:%s>' %(self.c_name,self.c_price,self.c_period))

# 创建课程对象
python=Course('python全栈开发',1900,'5mons')
linux=Course('linux架构师',900,'3mons')

stu1=OldboyStudent('刘二蛋',38,'male')
stu1.courses.append(python)
stu1.courses.append(linux)
# print(stu1.courses)
stu1.tell_all_course()

tea1=OldboyTeacher('egon',18,'male',10)
tea1.courses.append(python)
# print(tea1.courses)
tea1.tell_all_course()

三大特性之二:多态

多态与多态性

'''
1. 什么是多态
    多态指的是同一种/类事物的不同形态

2. 为何要用多态
    多态性:在多态的背景下,可以在不用考虑对象具体类型的前提下而直接使用对象
    多态性的精髓:统一

3. 如何用多态
'''
'''
class Animal:
    def speak(self):
        pass

class People(Animal):
    def shuo(self):
        print('say hello')

class Dog(Animal):
    def jiao(self):
        print('汪汪汪')

class Pig(Animal):
    def chang(self):
        print('哼哼哼')

obj1=People()
obj2=Dog()
obj3=Pig()


# obj1.speak()
# obj2.speak()
# obj3.speak()

def speak(animal):
    animal.speak()

speak(obj1)
speak(obj2)
speak(obj3)

s1='hello'
l1=[1,2,3]
t1=(1,2)

# changdu(s1)
# size(l1)
# kuangdu(t1)

print(len(s1)) #s1.__len__()
print(len(l1)) #l1.__len__()
print(len(t1)) #t1.__len__()
'''
import abc

class Animal(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def speak(self):
        pass

    @abc.abstractmethod
    def run(self):
        pass

# Animal() # 父类只是用来建立规范的,不能用来实例化的,更无需实现内部的方法

class People(Animal):
    def speak(self):
        print('say hello')

    def run(self):
        pass

class Dog(Animal):
    def speak(self):
        print('汪汪汪')

    def run(self):
        pass

class Pig(Animal):
    def speak(self):
        print('哼哼哼')

    def run(self):
        pass

obj1=People()
obj2=Dog()
obj3=Pig()


# python崇尚鸭子类型
class Disk:
    def read(self):
        print('Disk read')

    def write(self):
        print('Disk write')


class Memory:
    def read(self):
        print('Mem read')

    def write(self):
        print('Mem write')


class Cpu:
    def read(self):
        print('Cpu read')

    def write(self):
        print('Cpu write')


obj1=Disk()
obj2=Memory()
obj3=Cpu()

obj1.read()
obj2.read()
obj3.read()

三大特性之二:封装

封装

'''
1. 什么是封装
    装:往容器/名称空间里存入名字
    封:代表将存放于名称空间中的名字给藏起来,这种隐藏对外不对内

2. 为何要封装
    封数据属性:???
    封函数属性:???

3. 如何封装
    在类内定义的属性前加__开头(没有__结果)
    总结:
        1. __开头的属性实现的隐藏仅仅只是一种语法意义上的变形,并不会真的限制类外部的访问
        2. 该变形操作只在类定义阶段检测语法时发生一次,类定义阶段之后新增的__开头的属性并不会变形
        3. 如果父类不想让子类覆盖自己的属性,可以在属性前加__开头
'''
class Foo:
    __x=111 # _Foo__x
    __y=222 # _Foo__y

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

    def __func(self): #_Foo__func
        print('func')

    def get_info(self):
        print(self.__name,self.__age,self.__x) #print(self._Foo__name,self._Foo__age,self._Foo__x)

# print(Foo.__x)
# print(Foo.__func)
# print(Foo.__dict__)
# print(Foo._Foo__x)
# print(Foo._Foo__y)
# Foo.__z=333
# print(Foo.__dict__)
# print(Foo.__z)

obj=Foo('egon',18)
# print(obj.__dict__)
# print(obj.__name)
# print(obj.__age)
# obj.get_info()

obj.__sex='male'
# print(obj.__dict__)
# print(obj.__sex)

# obj.get_info()
# 1. __开头的属性到底如何实现的隐藏?
# 2. 如何实现的对外隐藏,对内不隐藏

class Foo:
    def __f1(self): #_Foo__f1
        print('Foo.f1')

    def f2(self):
        print('Foo.f2')
        self.__f1() #obj._Foo__f1()

class Bar(Foo):
    def __f1(self): # _Bar__f1 (与上面的__f1就不一样了)
        print('Bar.f1')

obj=Bar()
# obj.f2()
'''
Foo.f2
Foo.f1
'''

# 封装数据属性:将数据属性隐藏起来,类外就无法直接操作属性,需要类内开辟一个接口来外部的使用可以间接地操作属性,可以在接口内定义任意的控制逻辑,
# 从而严格控制使用对属性的操作
class People:
    def __init__(self,name,age):
        self.__name=name
        self.__age=age

    def tell_info(self): # 为了让外部能够访问到(这种访问是间接的,是提供给外部的)
        print('<name:%s age:%s>'  %(self.__name,self.__age))

    def set_info(self,name,age): # 为了让外部改数据(同上)
        if type(name) is not str:
            print('名字必须是str类型傻叉')
            return
        if type(age) is not int:
            print('年龄必须是int类型傻叉')
            return
        self.__name=name
        self.__age=age

obj=People('egon',18)
# obj.tell_info()
# obj.set_info('EGON',19)
# obj.set_info(123,19)
obj.set_info('EGON','18')
obj.tell_info()

# 封装函数属性:隔离复杂度
class ATM:
    def __card(self):
        print('插卡')
    def __auth(self):
        print('用户认证')
    def __input(self):
        print('输入取款金额')
    def __print_bill(self):
        print('打印账单')
    def __take_money(self):
        print('取款')

    def withdraw(self):
        self.__card()
        self.__auth()
        self.__input()
        self.__print_bill()
        self.__take_money()

a=ATM()
a.withdraw()

property

# property装饰器是用来将类内的函数属性伪装成数据属性
class People:
    def __init__(self,name,weight,height):
        self.name=name
        self.weight=weight
        self.height=height

    @property
    def bmi(self):
        return self.weight / (self.height ** 2)

obj=People('egon',80,1.83)
obj.height=1.85
obj.weight=75

print(obj.bmi()) # 没用装饰器之前
print(obj.bmi)  # 用过装饰器之后可以直接将bmi当做数据来看待

# 了解....
class People:
    def __init__(self,name):
        self.__name=name

    @property
    def name(self):
        return '<名字:%s>' %self.__name

    @name.setter #为对象修改属性,但在此之前要用property修饰过
    def name(self,obj):
        if type(obj) is not str:
            print('name必须为str类型')
            return
        self.__name=obj

    @name.deleter #删除属性操作
    def name(self):
        # print('不让删')
        del self.__name

obj=People('egon')

# print(obj.name)
# obj.name='EGON' 
# obj.name=123
# print(obj.name)
# del obj.name
# print(obj.__dict__)

# 另外一种写法
class People:
    def __init__(self,name):
        self.__name=name

    def get_name(self):
        return '<名字:%s>' %self.__name

    def set_name(self,obj):
        if type(obj) is not str:
            print('name必须为str类型')
            return
        self.__name=obj

    def del_name(self):
        # print('不让删')
        del self.__name
	#不推荐这种写法,建议用上面的写法
	name=property(get_name,set_name,del_name)

obj=People('egon')
print(obj.name)
obj.name='EGON'
obj.name=123
print(obj.name)
del obj.name
print(obj.__dict__)

绑定方法与非绑定方法

# 一: 绑定方法:绑定给谁就应该由谁来调用,谁来调用就会将谁当做第一个参数传入
# 1. 绑定给对象的方法: 类中定义的函数默认就是绑定给对象的
# 2. 绑定给类的方法: 为类中定义的函数加上一个装饰器classmethod

# 二: 非绑定方法: 既不与类绑定,又不与对象绑定,意味着对象和类都可以来调用,无论谁来调用都是一个普通的函数,没有自动传值的效果
class Foo:
    def f1(self):
        print(self)

    @classmethod
    def f2(cls):
        print(cls)

    @staticmethod
    def f3(x,y):
        print('f3',x+y)

obj=Foo()
print(obj.f1)
obj.f1()

print(Foo.f2)
Foo.f2()
print(obj.f2)
obj.f2()

print(Foo.f3)
print(obj.f3)

Foo.f3(1,2)
obj.f3(3,4)

# 应用
import settings

class MySql:
    def __init__(self, ip, port):
        self.id = self.create_id()
        self.ip = ip
        self.port = port

    def tell_info(self):
        print('<id:%s ip:%s port:%s>' % (self.id, self.ip, self.port))

    @classmethod #为类提供额外的实列化方式
    def from_conf(cls):
        return cls(settings.IP, settings.PORT)

    @staticmethod #既不需要传类也不需要传对象
    def create_id():
        import uuid
        return uuid.uuid4()

# obj1=MySql('1.1.1.1',3306)
# obj1.tell_info()
obj2 = MySql.from_conf()
obj2.tell_info()

面向对象串讲

'''
1 类与对象
    对象是特征与技能的结合体,而类则是一些列对象相同特征与技能的结合
    对象本质是一个容器/名称空间:用来存放对象特有的属性
    类的本质也是一个容器/名称空间:用来存放对象相同的属性

    类有两种用途:
        1. 当容器去用
            类的数据属性
            类的函数属性
        2. 调用类的过程称为实例化,结果就是对象
            实例化时方式两件事:
                1. 先产生一个空对象
                2. 触发__init__(空对象,arg1,arg2,...)

2 继承与派生
    1. 什么是继承
        继承是一种新建类的方式,新建的类称之为子类/派生类,被继承类称之为父类/超类/基类

        继承描述的是一种遗传的关系,特点:
            1. 子类可以遗传/重用父类的属性(减少代码冗余)
            2. 一个子类可以同时继承多个父类
            3. 在继承的背景下,类分为新式类与经典类

        子类如何重用父类属性?
        在继承背景下属性的查找顺序???
        新式类vs经典类

    2. 为何要用继承
        解决类与类之间代码冗余的问题

    3. 如何继承
        class Foo:
            pass

        class Bar(Foo):
            x=1

        obj=Bar()

    4. 在子类派生新方法中重用父类功能的两种方式:
        方式一:指名道姓地访问某一个类的函数
            特点:
                1. 与继承无关
                2. 没有自动传值的效果
        方式二:super(自己的类名,self)会得到一个特殊的对象,该对象专门用来访问"父类"中的属性,完全参照mro列表
            特点:
                1. 严格依赖继承,完全参照mro列表
                2. 有自动传值的效果

3 组合
    1. 什么是组合
        组合指的是一个对象拥有某一个属性,该属性的值是来自于另外一个类的对象
        class OldboyStudent:
            pass

        class OldboyTeacher:
            pass

        class Course:
            pass


        stu=OLdboyStudent()
        tea=OLdboyTeacher()

        obj=Course()

        stu.attr=obj
        tea.attr=obj

    2. 为何要用组合
        解决类与类之间代码冗余的问题

4 多态
    多态指的是同一种事物的多种形态,
    多态性指的是可以在不用考虑对象具体类型前提下而直接使用对象
    多态的精髓就是"统一"二字

    python崇尚鸭子类型

5 封装
    装
    封
'''
class Foo:
    x=1
    def __init__(self,name,age):
        self.name=name
        self.age=age

    def f1(self):
        print('run')

# print(Foo.__dict__)
obj=Foo('egon',18)
# print(obj.__dict__)

class A:
    def f1(self):
        print('A.f1')
        super().f2()

class B:
    def f2(self):
        print('B.f2')

class C(A,B):
    def f2(self):
        print('C.f2')

obj=C()
print(C.mro())
obj.f1()
'''
A.f1
'''

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

class Teacher(People):
    def __init__(self,name,age,sex,level):
        # People.__init__(self,name,age,sex)
        super().__init__(name,age,sex)
        self.level=level

obj=Teacher('egon',18,'male',18)
print(obj.__dict__)
posted @ 2019-05-29 22:40  Mrchenwang  阅读(95)  评论(0编辑  收藏  举报