面向对象

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

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

面向对象编程

核心对象二字,对象是特征与技能的结合体
基于该思想编写程序就好比是在创造一个世界,你就是这个世界的上帝,是一种上帝式的思维方式

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

类:

种类、分类、类别

对象是特征与技能的结合体,类是一系列对象相似的特征与技能的结合体
强调:站的角度不同,总结出的类是截然不同的

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

#1、先定义类
class OldboyStudent:
    school='oldboy'

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

#强调:类定义阶段会立刻执行类体代码,会产生类的名称空间,将类体代码执行过程中产生的名字都丢进去
# print(OldboyStudent.__dict__)
# 类本质就是一个名称空间/容器,从类的名称空间中增/删/改/查名字
# python为我们提供专门访问属性(名称空间中的名字)的语法,点后的都是属性
# OldboyStudent.school #OldboyStudent.__dict__['school']
# OldboyStudent.x=1 #OldboyStudent.__dict__['x']=1
# OldboyStudent.school='Oldboy' #OldboyStudent.__dict__['school']='Oldboy'
# del OldboyStudent.x #del OldboyStudent.__dict__['x']

# 类中定义的函数是类的函数属性,类可以使用,但使用的就是一个普通的函数而已,意味着需要完全遵循函数的参数规则,该传几个值就传几个
# OldboyStudent.choose_course(123)

#2、后调用类产生对象,调用类的过程称之为实例化,实例化的结果称为类的一个实例或者对象
stu1=OldboyStudent()
stu2=OldboyStudent()
stu3=OldboyStudent()
# print(stu1)
# print(stu2)
# print(stu3)

# print(OldboyStudent.school)
# OldboyStudent.school='OLDBOY'
# print(stu1.school)
# print(stu2.school)
# print(stu3.school)

对象初始化自己属性

'''
# 例1
class OldboyStudent:
    school='oldboy'

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

stu1=OldboyStudent()
stu2=OldboyStudent()
stu3=OldboyStudent()

#对象本质也就是一个名称空间而已,对象名称空间是用存放对象自己独有的名字/属性,而
#类中存放的是对象们共有的属性
# print(stu1.__dict__)
# print(stu2.__dict__)
# print(stu3.__dict__)

stu1.name='耗哥'
stu1.age=18
stu1.sex='male'
# print(stu1.name,stu1.age,stu1.sex)
# print(stu1.__dict__)

stu2.name='猪哥'
stu2.age=17
stu2.sex='male'

stu3.name='帅翔'
stu3.age=19
stu3.sex='female'


# 例2
class OldboyStudent:
    school='oldboy'

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

stu1=OldboyStudent()
stu2=OldboyStudent()
stu3=OldboyStudent()

def init(obj,x,y,z):
    obj.name=x
    obj.age=y
    obj.sex=z

# stu1.name='耗哥'
# stu1.age=18
# stu1.sex='male'
init(stu1,'耗哥',18,'male')

# stu2.name='猪哥'
# stu2.age=17
# stu2.sex='male'
init(stu2,'诸哥',17,'male')

# stu3.name='帅翔'
# stu3.age=19
# stu3.sex='female'
init(stu3,'帅翔',19,'female')


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


class OldboyStudent:
    school='oldboy'


    def __init__(obj, x, y, z): #会在调用类时自动触发
        obj.name = x #stu1.name='耗哥'
        obj.age = y  #stu1.age=18
        obj.sex = z #stu1.sex='male'

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

#调用类时发生两件事
#1、创造一个空对象stu1
#2、自动触发类中__init__功能的执行,将stu1以及调用类括号内的参数一同传入
stu1=OldboyStudent('耗哥',18,'male') #OldboyStudent.__init__(stu1,'耗哥',18,'male')
stu2=OldboyStudent('猪哥',17,'male')
stu3=OldboyStudent('帅翔',19,'female')


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

属性查找

class OldboyStudent:
    school='oldboy'
    count=0

    def __init__(self, x, y, z): #会在调用类时自动触发
        self.name = x #stu1.name='耗哥'
        self.age = y  #stu1.age=18
        self.sex = z #stu1.sex='male'
        OldboyStudent.count+=1

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


# 先从对象自己的名称空间找,没有则去类中找,如果类也没有则报错
stu1=OldboyStudent('耗哥',18,'male')
stu2=OldboyStudent('猪哥',17,'male')
stu3=OldboyStudent('帅翔',19,'female')

print(OldboyStudent.count)
print(stu1.count)
print(stu2.count)
print(stu3.count)

绑定方法

class OldboyStudent:
    school='oldboy'


    def __init__(self, x, y, z): #会在调用类时自动触发
        self.name = x #stu1.name='耗哥'
        self.age = y  #stu1.age=18
        self.sex = z #stu1.sex='male'

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

    def func():
        pass
# 类名称空间中定义的数据属性和函数属性都是共享给所有对象用的
# 对象名称空间中定义的只有数据属性,而且时对象所独有的数据属性

stu1=OldboyStudent('耗哥',18,'male')
stu2=OldboyStudent('猪哥',17,'male')
stu3=OldboyStudent('帅翔',19,'female')

# print(stu1.name)
# print(stu1.school)


# 类中定义的函数是类的函数属性,类可以使用,但使用的就是一个普通的函数而已,意味着需要完全遵循函数的参数规则,该传几个值就传几个
# print(OldboyStudent.choose_course)
# OldboyStudent.choose_course(123)

# 类中定义的函数是共享给所有对象的,对象也可以使用,而且是绑定给对象用的,
#绑定的效果:绑定给谁,就应该由谁来调用,谁来调用就会将谁当作第一个参数自动传入
# print(id(stu1.choose_course))
# print(id(stu2.choose_course))
# print(id(stu3.choose_course))
# print(id(OldboyStudent.choose_course))

# print(id(stu1.school))
# print(id(stu2.school))
# print(id(stu3.school))
#
# print(id(stu1.name),id(stu2.name),id(stu3.name))


# stu1.choose_course(1)
# stu2.choose_course(2)
# stu3.choose_course(3)
stu1.func()

# 补充:类中定义的函数,类确实可以使用,但其实类定义的函数大多情况下都是绑定给对象用的,所以在类中定义的函数都应该自带一个参数self

类与类型

#在python3中统一了类与类型的概念,类就是类型
class OldboyStudent:
    school='oldboy'

    def __init__(self, x, y, z): #会在调用类时自动触发
        self.name = x #stu1.name='耗哥'
        self.age = y  #stu1.age=18
        self.sex = z #stu1.sex='male'

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

stu1=OldboyStudent('耗哥',18,'male')
# stu1.choose_course(1) #OldboyStudent.choose_course(stu1,1)
# OldboyStudent.choose_course(stu1,1)


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

继承与派生

'''
1、什么是继承
    继承是一种新建类的方式,新建的类称为子类,被继承的类称为父类
    继承的特性是:子类会遗传父类的属性
    强调:继承是类与类之间的关系

2、为什么用继承
    继承的好处就是可以减少代码的冗余

3、如何用继承
    在python中支持一个类同时继承多个父类
    在python3中
        如果一个类没有继承任何类,那默认继承object类
    在python2中:
        如果一个类没有继承任何类,不会继承object类

    新式类
        但凡继承了object的类以及该类的子类,都是新式类
    经典类
        没有继承object的类以及该类的子类,都是经典类

    在python3中都是新式类,只有在python2中才区别新式类与经典类

    新式类vs经典类?

'''
class Parent1(object):
    pass

class Parent2(object):
    pass

class Sub1(Parent1,Parent2):
    pass

# print(Sub1.__bases__)
print(Parent1.__bases__)
print(Parent2.__bases__)

继承的应用

#派生:子类中新定义的属性,子类在使用时始终以自己的为准
class OldboyPeople:
    school = 'oldboy'
    def __init__(self,name,age,sex):
        self.name = name #tea1.name='egon'
        self.age = age #tea1.age=18
        self.sex = sex #tea1.sex='male'



class OldboyStudent(OldboyPeople):
    def choose_course(self):
        print('%s is choosing course' %self.name)


class OldboyTeacher(OldboyPeople):
    #            tea1,'egon',18,'male',10
    def __init__(self,name,age,sex,level):
        # self.name=name
        # self.age=age
        # self.sex=sex
        OldboyPeople.__init__(self,name,age,sex)
        self.level=level

    def score(self,stu_obj,num):
        print('%s is scoring' %self.name)
        stu_obj.score=num

stu1=OldboyStudent('耗哥',18,'male')
tea1=OldboyTeacher('egon',18,'male',10)

#对象查找属性的顺序:对象自己-》对象的类-》父类-》父类。。。
# print(stu1.school)
# print(tea1.school)
# print(stu1.__dict__)
# print(tea1.__dict__)

tea1.score(stu1,99)

print(stu1.__dict__)


# 在子类派生出的新功能中重用父类功能的方式有两种:
#1、指名道姓访问某一个类的函数:该方式与继承无关


# class Foo:
#     def f1(self):
#         print('Foo.f1')
#
#     def f2(self):
#         print('Foo.f2')
#         self.f1()
#
# class Bar(Foo):
#     def f1(self):
#         print('Bar.f1')
#
# #对象查找属性的顺序:对象自己-》对象的类-》父类-》父类。。。
# obj=Bar()
# obj.f2()
# '''
# Foo.f2
# Bar.f1
# '''

 组合

组合指的是,在一个类中以另外一个类的对象作为数据属性,称为类的组合

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,stu_id):
        OldboyPeople.__init__(self,name,age,sex)
        self.stu_id=stu_id

    def choose_course(self):
        print('%s is 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
        print('老师[%s]为学生[%s]打分[%s]' %(self.name,stu.name,num))


stu1=OldboyStudent('猪哥',19,'male',1)
tea1=OldboyTeacher('egon',18,'male',10)

stu1.choose_course()
tea1.score(stu1,100)
print(stu1.__dict__)

'''
class Course:
    def __init__(self,name,period,price):
        self.name=name
        self.period=period
        self.price=price

    def tell_info(self):
        msg="""
        课程名:%s
        课程周期:%s
        课程价钱:%s
        """ %(self.name,self.period,self.price)
        print(msg)

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,stu_id):
        OldboyPeople.__init__(self,name,age,sex)
        self.stu_id=stu_id

    def choose_course(self):
        print('%s is 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
        print('老师[%s]为学生[%s]打分[%s]' %(self.name,stu.name,num))

# 创造课程
python=Course('python全栈开发','5mons',3000)
linux=Course('linux运维','5mons',800)
# python.tell_info()
# linux.tell_info()


# 创造学生与老师
stu1=OldboyStudent('猪哥',19,'male',1)
tea1=OldboyTeacher('egon',18,'male',10)


# 将学生、老师与课程对象关联/组合
stu1.course=python
tea1.course=linux

stu1.course.tell_info()
tea1.course.tell_info()

菱形继承问题

1、菱形继承
当一个子继承多个父类时,多个父类最终继承了同一个类,称之为菱形继承

2、菱形继承的问题:
python2区分经典类与新式类,如果子的继承是一个菱形继承,那么经典类与形式的区别为?
经典类下查找属性:深度优先查找
新式类下查找属性:广度优先查找

class G(object):
    # def test(self):
    #     print('from G')
    pass

class E(G):
    # def test(self):
    #     print('from E')
    pass

class B(E):
    # def test(self):
    #     print('from B')
    pass

class F(G):
    # def test(self):
    #     print('from F')
    pass

class C(F):
    # def test(self):
    #     print('from C')
    pass

class D(G):
    # def test(self):
    #     print('from D')
    pass

class A(B,C,D):
    def test(self):
        print('from A')
    # pass

obj=A()
print(A.mro())
# obj.test() #A->B->E-C-F-D->G-object

子类调用父类的方法

# 在子派生的新方法中重用父类功能的两种方式
# 方式一:与继承无关
#指名道姓法,直接用:类名.函数名
# 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,stu_id):
#         OldboyPeople.__init__(self,name,age,sex)
#         self.stu_id=stu_id
#
#     def choose_course(self):
#         print('%s is choosing course' %self.name)


# 方式二:严格以来继承属性查找关系
# super()会得到一个特殊的对象,该对象就是专门用来访问父类中的属性的(按照继承的关系)
# super().__init__(不用为self传值)
# 注意:
# super的完整用法是super(自己的类名,self),在python2中需要写完整,而python3中可以简写为super()
# 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,stu_id):
#         # OldboyPeople.__init__(self,name,age,sex)
#         super(OldboyStudent,self).__init__(name,age,sex)
#         self.stu_id=stu_id
#
#     def choose_course(self):
#         print('%s is choosing course' %self.name)
#
#
# stu1=OldboyStudent('猪哥',19,'male',1)
# print(stu1.__dict__)
#
# print(OldboyStudent.mro())


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

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

class C(B,A):
    pass

obj=C()
print(C.mro()) #C-》B->A->object
obj.f2()

当你使用super()函数时,Python会在MRO列表上继续搜索下一个类。只要每个重定义的方法统一使用super()并只调用它一次,那么控制流最终会遍历完整个MRO列表,每个方法也只会被调用一次(注意注意注意:使用super调用的所有属性,都是从MRO列表当前的位置往后找,千万不要通过看代码去找继承关系,一定要看MRO列表

多态和多态性

'''
1 什么是多态
    多态指的是同一种事物的多种形态
        水-》冰、水蒸气、液态水
        动物-》人、狗、猪

2 为和要用多态
    多态性:
    继承同一个类的多个子类中有相同的方法名
    那么子类产生的对象就可以不用考虑具体的类型而直接调用功能

3 如何用
'''
import abc

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

# Animal() #强调:父类是用来指定标准的,不能被实例化

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

    def eat(self):
        pass

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

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

    def eat(self):
        pass



peo1=People()
dog1=Dog()
pig1=Pig()
#
#
# peo1.speak()
# dog1.speak()
# pig1.speak()

# def my_speak(animal):
#     animal.speak()
#
# my_speak(peo1)
# my_speak(dog1)
# my_speak(pig1)

#
# l=[1,2,3]
# s='helllo'
# t=(1,2,3)
#
# print(l.__len__())
# print(s.__len__())
# print(t.__len__())
#
# # def len(obj):
# #     return obj.__len__()
#
# print(len(l)) # l.__len__()
# print(len(s)) #s.__len__()
# print(len(t))

# python推崇的是鸭子类型,只要你叫的声音像鸭子,并且你走路的样子也像鸭子,那你就是鸭子

class Disk:
    def read(self):
        print('disk read')

    def write(self):
        print('disk wirte')


class Process:
    def read(self):
        print('process read')

    def write(self):
        print('process wirte')


class File:
    def read(self):
        print('file read')

    def write(self):
        print('file wirte')



obj1=Disk()
obj2=Process()
obj3=File()



obj1.read()
obj1.write()

 封装

'''
1、什么封装
    封:属性对外是隐藏的,但对内是开放的
    装:申请一个名称空间,往里装入一系列名字/属性

2、为什么要封装
    封装数据属性的目的
        首先定义属性的目的就是为了给类外部的使用使用的,
        隐藏之后是为了不让外部使用直接使用,需要类内部开辟一个接口
        然后让类外部的使用通过接口来间接地操作隐藏的属性。
        精髓在于:我们可以在接口之上附加任意逻辑,从而严格控制使用者对属性的操作

    封装函数属性
        首先定义属性的目的就是为了给类外部的使用使用的,
        隐藏函数属性是为了不让外不直接使用,需要类内部开辟一个接口
        然后在接口内去调用隐藏的功能
        精髓在于:隔离了复杂度



3、如何封装

'''
# 如何隐藏:在属性前加上__开头


#1、 这种隐藏仅仅只是一种语法上的变形操作
#2、 这种语法上的变形只在类定义阶段发生一次,因为类体代码仅仅只在类定义阶段检测一次
#3、 这种隐藏是对外不对内的,即在类的内部可以直接访问,而在类的外则无法直接访问,原因是
#    在类定义阶段,类体内代码统一发生了一次变形

#4、 如果不想让子类的方法覆盖父类的,可以将该方法名前加一个__开头


# class People:
#     __country='China' #_People__country='China'
#     __n=100 #_People__n=100
#     def __init__(self,name,age,sex):
#         self.__name=name #self._People__name=name
#         self.age=age
#         self.sex=sex
#
#     def eat(self):
#         print('eat.....')
#         print(People.__country) #People._People__country
#         print(self.__name) #self._People__name

# People.eat(123)
# print(People.__country)

# peo1=People('egon',18,'male')
# peo1.eat()
# print(peo1.__name)

# print(People.__dict__)
# print(People.__country)
# print(People._People__country)

# People.__x=11
# print(People.__dict__)


# peo1=People('egon',18,'male')
# print(peo1.__dict__)
# peo1.__x=111
# print(peo1.__dict__)

# class Foo:
#     def __f1(self): #_Foo__f1
#         print('Foo.f1')
#
#     def f2(self):
#         print('Foo.f2')
#         self.__f1() #self._Foo__f1
#
# class Bar(Foo):
#     def __f1(self): #_Bar__f1
#         print('Bar.f1')
#
# obj=Bar()
# obj.f2()




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

    def tell_info(self):
        print('%s:%s' %(self.__name,self.__age))

    def set_info(self,name,age):
        if type(name) is not str:
            # print('用户名必须为str类型')
            # return
            raise TypeError('用户名必须为str类型')

        if type(age) is not int:
            # print('年龄必须为int类型')
            # return
            raise TypeError('年龄必须为int类型')
        self.__name=name
        self.__age=age

peo1=People('egon',18)
# peo1.name=123
# peo1.age
# peo1.tell_info()

peo1.set_info('egon',19)
# peo1.tell_info()

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)
#
# peo1=People('egon',75,1.8)
#
# peo1.height=1.85
# print(peo1.bmi)


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

    @property # 查看obj.name
    def name(self):
        return '<名字是:%s>' %self.__name

    @name.setter #修改obj.name=值
    def name(self,name):
        if type(name) is not str:
            raise TypeError('名字必须是str类型傻叉')
        self.__name=name

    @name.deleter #删除del obj.name
    def name(self):
        # raise PermissionError('不让删')
        print('不让删除傻叉')
        # del self.__name

peo1=People('egon')
# print(peo1.name)

# print(peo1.name)

# peo1.name='EGON'
# print(peo1.name)

del peo1.name

'''


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


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

    def set_name(self,name):
        if type(name) is not str:
            raise TypeError('名字必须是str类型傻叉')
        self.__name=name

    def del_name(self):
        print('不让删除傻叉')

    name=property(tell_name,set_name,del_name)


peo1=People('egon')

print(peo1.name)
peo1.name='EGON'
print(peo1.name)
del peo1.name

 绑定方法与非绑定方法

'''
1、绑定方法
    特性:绑定给谁就应该由谁来调用,谁来调用就会将谁当作第一个参数自动传入
         《《《精髓在于自动传值》》》

    绑定方法分为两类:
        1.1 绑定给对象方法
            在类内部定义的函数(没有被任何装饰器修饰的),默认就是绑定给对象用的
        1.2 绑定给类的方法:
            在类内部定义的函数如果被装饰器@classmethod装饰,
            那么则是绑定给类的,应该由类来调用,类来调用就自动将类当作第一个参数自动传入



2、非绑定方法
    类中定义的函数如果被装饰器@staticmethod装饰,那么该函数就变成非绑定方法
    既不与类绑定,又不与对象绑定,意味着类与对象都可以来调用
    但是无论谁来调用,都没有任何自动传值的效果,就是一个普通函数





3 应用
    如果函数体代码需要用外部传入的类,则应该将该函数定义成绑定给类的方法
    如果函数体代码需要用外部传入的对象,则应该将该函数定义成绑定给对象的方法
    如果函数体代码既不需要外部传入的类也不需要外部传入的对象,则应该将该函数定义成非绑定方法/普通函数


'''

# class Foo:
#     @classmethod
#     def f1(cls):
#         print(cls)
#
#     def f2(self):
#         print(self)
#
#
# obj=Foo()
# print(obj.f2)
# print(Foo.f1)

# Foo.f1()
# print(Foo)


#1、f1绑定给类的
# 了解:绑定给类的应该由类来调用,但对象其实也可以使用,只不过自动传入的仍然是类
# print(Foo.f1)
# print(obj.f1)
# Foo.f1()
# obj.f1()

#2、f2是绑定给对象的
# obj.f2()
# Foo.f2(obj)

import settings
import uuid

class Mysql:
    def __init__(self,ip,port):
        self.uid=self.create_uid()
        self.ip=ip
        self.port=port

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

    @classmethod
    def from_conf(cls):
        return cls(settings.IP, settings.PORT)

    @staticmethod
    def func(x,y):
        print('不与任何人绑定')

    @staticmethod
    def create_uid():
        return uuid.uuid1()

# 默认的实例化方式:类名(..)
obj=Mysql('10.10.0.9',3307)

# 一种新的实例化方式:从配置文件中读取配置完成实例化
# obj1=Mysql.from_conf()
# obj1.tell_info()

# obj.func(1,2)
# Mysql.func(3,4)
# print(obj.func)
# print(Mysql.func)

print(obj.uid)

 补充内置函数

class Foo:
    pass

obj=Foo()

print(isinstance(obj,Foo))

# 在python3中统一类与类型的概念
# d={'x':1} #d=dict({'x':1} #)

# print(type(d) is dict)
# print(isinstance(d,dict))

# issubclass()


class Parent:
    pass

class Sub(Parent):
    pass

print(issubclass(Sub,Parent))
print(issubclass(Parent,object))

 反射

'''
1、什么是反射
    通过字符串来操作类或者对象的属性

2、如何用
    hasattr
    getattr
    setattr
    delattr


'''

class People:
    country='China'
    def __init__(self,name):
        self.name=name

    def eat(self):
        print('%s is eating' %self.name)

peo1=People('egon')


# print(hasattr(peo1,'eat')) #peo1.eat

# print(getattr(peo1,'eat')) #peo1.eat
# print(getattr(peo1,'xxxxx',None))

# setattr(peo1,'age',18) #peo1.age=18
# print(peo1.age)

# print(peo1.__dict__)
# delattr(peo1,'name') #del peo1.name
# print(peo1.__dict__)


class Ftp:
    def __init__(self,ip,port):
        self.ip=ip
        self.port=port

    def get(self):
        print('GET function')

    def put(self):
        print('PUT function')

    def run(self):
        while True:
            choice=input('>>>: ').strip()
            # print(choice,type(choice))
            # if hasattr(self,choice):
            #     method=getattr(self,choice)
            #     method()
            # else:
            #     print('输入的命令不存在')

            method=getattr(self,choice,None)
            if method is None:
                print('输入的命令不存在')
            else:
                method()

conn=Ftp('1.1.1.1',23)
conn.run()

__setattr__,__delattr__,__getattr__

class Foo:
    x=1
    def __init__(self,y):
        self.y=y

    def __getattr__(self, item):
        print('----> from getattr:你找的属性不存在')


    def __setattr__(self, key, value):
        print('----> from setattr')
        # self.key=value #这就无限递归了,你好好想想
        # self.__dict__[key]=value #应该使用它

    def __delattr__(self, item):
        print('----> from delattr')
        # del self.item #无限递归了
        self.__dict__.pop(item)

#__setattr__添加/修改属性会触发它的执行
f1=Foo(10)
print(f1.__dict__) # 因为你重写了__setattr__,凡是赋值操作都会触发它的运行,你啥都没写,就是根本没赋值,除非你直接操作属性字典,否则永远无法赋值
f1.z=3
print(f1.__dict__)

#__delattr__删除属性的时候会触发
f1.__dict__['a']=3#我们可以直接修改属性字典,来完成添加/修改属性的操作
del f1.a
print(f1.__dict__)

#__getattr__只有在使用点调用属性且属性不存在的时候才会触发
f1.xxxxxx

__str__,__repr__,__format__

改变对象的字符串显示__str__,__repr__

自定制格式化字符串__format__

#_*_coding:utf-8_*_
__author__ = 'Linhaifeng'
format_dict={
    'nat':'{obj.name}-{obj.addr}-{obj.type}',#学校名-学校地址-学校类型
    'tna':'{obj.type}:{obj.name}:{obj.addr}',#学校类型:学校名:学校地址
    'tan':'{obj.type}/{obj.addr}/{obj.name}',#学校类型/学校地址/学校名
}
class School:
    def __init__(self,name,addr,type):
        self.name=name
        self.addr=addr
        self.type=type

    def __repr__(self):
        return 'School(%s,%s)' %(self.name,self.addr)
    def __str__(self):
        return '(%s,%s)' %(self.name,self.addr)

    def __format__(self, format_spec):
        # if format_spec
        if not format_spec or format_spec not in format_dict:
            format_spec='nat'
        fmt=format_dict[format_spec]
        return fmt.format(obj=self)

s1=School('oldboy1','北京','私立')
print('from repr: ',repr(s1))  #from repr: School(oldboy1,北京)
print('from str: ',str(s1))    #from str: (oldboy1,北京)
print(s1)  #(oldboy1,北京)
'''
str函数或者print函数--->obj.__str__()
repr或者交互式解释器--->obj.__repr__()
如果__str__没有被定义,那么就会使用__repr__来代替输出
注意:这俩方法的返回值必须是字符串,否则抛出异常
'''
print(format(s1,'nat'))
print(format(s1,'tna'))
print(format(s1,'tan'))
print(format(s1,'asfdasdffd'))
自定义format练习
date_dic={
    'ymd':'{0.year}:{0.month}:{0.day}',
    'dmy':'{0.day}/{0.month}/{0.year}',
    'mdy':'{0.month}-{0.day}-{0.year}',
}
class Date:
    def __init__(self,year,month,day):
        self.year=year
        self.month=month
        self.day=day

    def __format__(self, format_spec):
        if not format_spec or format_spec not in date_dic:
            format_spec='ymd'
        fmt=date_dic[format_spec]
        return fmt.format(self)

d1=Date(2016,12,29)
print(format(d1))
print('{:mdy}'.format(d1))


 自定义内置方法定制类

#1、__str__方法
# class People:
#     def __init__(self,name,age):
#         self.name=name
#         self.age=age
#
#     #在对象被打印时,自动触发,应该在该方法内采集与对象self有关的信息,然后拼成字符串返回
#     def __str__(self):
#         # print('======>')
#         return '<name:%s age:%s>' %(self.name,self.age)
# obj=People('egon',18)
# obj1=People('alex',18)
# print(obj)  #obj.__str__()
# print(obj)  #obj.__str__()
# print(obj)  #obj.__str__()
# print(obj1)  #obj1.__str__()


# d={'x':1} #d=dict({'x':1})
# print(d)


#1、__del__析构方法
# __del__会在对象被删除之前自动触发
class People:
    def __init__(self,name,age):
        self.name=name
        self.age=age
        self.f=open('a.txt','rt',encoding='utf-8')

    def __del__(self):
        # print('run=-====>')
        # 做回收系统资源相关的事情
        self.f.close()

obj=People('egon',18)

print('')

元类

'''
1、什么是元类
    在python中一切皆对象,那么我们用class关键字定义的类本身也是一个对象
    负责产生该对象的类称之为元类,即元类可以简称为类的类

    class Foo: # Foo=元类()
        pass
2、为何要用元类
    元类是负责产生类的,所以我们学习元类或者自定义元类的目的
    是为了控制类的产生过程,还可以控制对象的产生过程

3、如何用元类

'''
#1、储备知识:内置函数exec的用法
# cmd="""
# x=1
# def func(self):
#     pass
# """
# class_dic={}
# exec(cmd,{},class_dic)
#
# print(class_dic)

#2、创建类的方法有两种
# 大前提:如果说类也是对象的化,那么用class关键字的去创建类的过程也是一个实例化的过程
# 该实例化的目的是为了得到一个类,调用的是元类
#2.1 方式一:用的默认的元类type
# class People: #People=type(...)
#     country='China'
#     def __init__(self,name,age):
#         self.name=name
#         self.age=age
#
#     def eat(self):
#         print('%s is eating' %self.name)

# print(type(People))

#2.1.1 创建类的3个要素:类名,基类,类的名称空间
class_name='People'
class_bases=(object,)
class_dic={}
class_body="""
country='China'
def __init__(self,name,age):
    self.name=name
    self.age=age

def eat(self):
    print('%s is eating' %self.name)
"""
exec(class_body,{},class_dic)

# 准备好创建类的三要素
# print(class_name)
# print(class_bases)
# print(class_dic)

# People=type(类名,基类,类的名称空间)
# People1=type(class_name,class_bases,class_dic)
# print(People1)
# obj1=People1('egon',18)
# print(People)
# obj=People('egon',18)
#
# obj1.eat()
# obj.eat()

'''
#2.2 方式二:用的自定义的元类
class Mymeta(type): #只有继承了type类才能称之为一个元类,否则就是一个普通的自定义类
    def __init__(self,class_name,class_bases,class_dic):
        print(self) #现在是People
        print(class_name)
        print(class_bases)
        print(class_dic)
        super(Mymeta,self).__init__(class_name,class_bases,class_dic) #重用父类的功能

# 分析用class自定义类的运行原理(而非元类的的运行原理):
#1、拿到一个字符串格式的类名class_name='People'
#2、拿到一个类的基类们class_bases=(obejct,)
#3、执行类体代码,拿到一个类的名称空间class_dic={...}
#4、调用People=type(class_name,class_bases,class_dic)
class People(object,metaclass=Mymeta): #People=Mymeta(类名,基类们,类的名称空间)
    country='China'
    def __init__(self,name,age):
        self.name=name
        self.age=age

    def eat(self):
        print('%s is eating' %self.name)


# 应用:自定义元类控制类的产生过程,类的产生过程其实就是元类的调用过程

class Mymeta(type): #只有继承了type类才能称之为一个元类,否则就是一个普通的自定义类
    def __init__(self,class_name,class_bases,class_dic):
        if class_dic.get('__doc__') is None or len(class_dic.get('__doc__').strip()) == 0:
            raise TypeError('类中必须有文档注释,并且文档注释不能为空')
        if not class_name.istitle():
            raise TypeError('类名首字母必须大写')
        super(Mymeta,self).__init__(class_name,class_bases,class_dic) #重用父类的功能

class People(object,metaclass=Mymeta): #People=Mymeta('People',(object,),{....})
    """这是People类"""
    country='China'
    def __init__(self,name,age):
        self.name=name
        self.age=age

    def eat(self):
        print('%s is eating' %self.name)
'''


#3 储备知识:__call__
# class Foo:
#     def __call__(self, *args, **kwargs):
#         print(self)
#         print(args)
#         print(kwargs)
#
#
# obj=Foo()
#
# # 要想让obj这个对象变成一个可调用的对象,需要在该对象的类中定义一个方法__call__方法
# # 该方法会在调用对象时自动触发
# obj(1,2,3,x=1,y=2)


# 4、自定义元类来控制类的调用的过程,即类的实例化过程
class Mymeta(type):

    def __call__(self, *args, **kwargs):
        # print(self) # self是People
        # print(args)
        # print(kwargs)
        # return 123

        # 1、先造出一个People的空对象
        obj=self.__new__(self)
        # 2、为该对空对象初始化独有的属性
        # print(args,kwargs)
        self.__init__(obj,*args,**kwargs)

        # 3、返回一个初始好的对象
        return obj


class People(object,metaclass=Mymeta):
    country='China'
    def __init__(self,name,age):
        self.name=name
        self.age=age

    def eat(self):
        print('%s is eating' %self.name)

    def __new__(cls, *args, **kwargs):
        print(cls)
        # cls.__new__(cls) # 错误
        obj=super(People,cls).__new__(cls)
        return obj

# 分析:调用Pepole的目的
#1、先造出一个People的空对象
#2、为该对空对象初始化独有的属性
# obj1=People('egon1',age=18)
# obj2=People('egon2',age=18)
# print(obj1)
# print(obj2)

obj=People('egon',age=18)
print(obj.__dict__)
print(obj.name)
obj.eat()

  

原文地址:https://www.cnblogs.com/hanbowen/p/9224724.html