第三模块第24章 面向对象

book.luffycity.com/python-book/index.html

https://www.cnblogs.com/linhaifeng/articles/6182264.html

1.3 编程范式

# 编程范式:
# 按照某种语法风格+数据结构+算法来编写程序

1.4 面向过程编程

面向过程: 核心是过程二字, 过程指的是解决问题的步骤, 设计一条流水线, 机械式的思维.

优点: 复杂的问题流程化, 进而简单化

缺点: 可扩展性差

1.5 面向对象编程介绍

面向对象: 核心是对象二字, 对象是特征与技能的结合体

优点: 可扩展性强

缺点: 编程复杂度高

应用场景: 用户需求经常变化, 如互联网应用, 游戏, 企业内部应用

1.6 定义类与实例化出对象

类是一系列对象相似特征与技能的结合体

强调: 站在不同的角度, 得到的分类是不一样的

在现实世界中, 先有对象, 后有类

在程序中, 先定义类, 后调用类来产生对象

# 先定义类
class LuffyStudent:
    school = 'luffycity'
    def learn(self):
        print('is learning')
    def eat(self):
        print('is eating')
    def sleep(self):
        print('is sleeping')
# 后实例化出对象
stu1 = LuffyStudent()
stu2 = LuffyStudent()
print(stu1)  # <__main__.LuffyStudent object at 0x000001EB53893D68>
print(stu2)  # <__main__.LuffyStudent object at 0x000001EB538C6FD0>

 

1.7 如何使用类

'''
类在定义阶段就会运行代码
类内定义的变量称为数据属性, 定义的函数称为函数属性
'''
class LuffyStudent:
    school = 'luffycity'  # 数据属性
    def learn(self):  # 函数属性
        print('is learning')
    def eat(self):
        print('is eating')
    def sleep(self):
        print('is sleeping')

# 在类的定义阶段代码就会运行
print(LuffyStudent.__dict__)  # 查看类的名称空间
'''
结果:
{'__module__': '__main__', 
'school': 'luffycity', 'learn': <function LuffyStudent.learn at 0x0000019A2BC41488>, 'eat': <function LuffyStudent.eat at 0x0000019A2BC41A60>, 'sleep': <function LuffyStudent.sleep at 0x0000019A2BC41B70>, '__dict__': <attribute '__dict__' of 'LuffyStudent' objects>, '__weakref__': <attribute '__weakref__' of 'LuffyStudent' objects>, '__doc__': None}
''' # 1. 查看类的属性 print(LuffyStudent.school) # luffycity print(LuffyStudent.learn) # <function LuffyStudent.learn at 0x0000019A2BC41488> # 2. 增加属性 LuffyStudent.country = 'china' # 3. 删除属性 del LuffyStudent.country # 4. 修改属性 LuffyStudent.school = 'Luffycity'

 

1.8 如何使用对象

# __init__方法用来为对象定制对象独有的特征
class LuffyStudent:
    school = 'luffycity'  # 对象共有的特征
    def __init__(self, name, gender, age):  # 实例化对象时自动调用本方法
        self.name = name
        self.gender = gender
        self.age = age
    def learn(self):
        print('is learning')
    def eat(self):
        print('is eating')
    def sleep(self):
        print('is sleeping')
stu1 = LuffyStudent('egon','male',18)

# 加上__init__后, 实例化的步骤
# 1. 先产生一个空对象
# 2. 触发Luffycity.__init__, 将空对象传给self, 并将后面的参数传进来

print(stu1.__dict__)  # 查看对象stu1的名称空间
# 结果: {'name': 'egon', 'gender': 'male', 'age': 18}

# 1.查看对象的属性
print(stu1.name)

# 2. 修改对象的属性
stu1.name = 'alex'

# 3. 删除
del stu1.name

# 4. 增加
stu1.name = 'egon'

1.9 属性查找与绑定方法

class LuffyStudent:
    school = 'luffycity'
    def __init__(self, name, gender, age):
        self.name = name
        self.gender = gender
        self.age = age
    def learn(self, x):
        print('%s is learning %s'%(self.name,x))
    def eat(self):
        print('%s is eating'%self.name)
    def sleep(self):
        print('%s is sleeping'%self.name)
stu1 = LuffyStudent('egon','male',18)
stu2 = LuffyStudent('alex','male',28)

# 类中的数据属性: 是所有对象共有的
print(LuffyStudent.school, id(LuffyStudent.school))  # luffycity 2210760573872, 类可以访问
print(stu1.school, id(stu1.school))  # luffycity 2210760573872, 对象可以访问

# 类中的函数属性: 是绑定给对象使用的, 绑定到不同的对象是不同的绑定方法, 对象调用绑定方法时会将对象本身当成第一个参数传入
print(LuffyStudent.learn)  # <function LuffyStudent.learn at 0x00000214BB8E1B70>
print(stu1.learn)  # <bound method LuffyStudent.learn of <__main__.LuffyStudent object at 0x00000214BB8DF978>>
print(stu2.learn)  # <bound method LuffyStudent.learn of <__main__.LuffyStudent object at 0x00000214BB8DF9B0>>

LuffyStudent.learn(stu1, '英语')  # 类名调用函数, 需将对象传进去, egon is learning 英语
stu1.learn('数学')  # 对象调用方法, 无需传对象, 只需传self后的参数, egon is learning 数学
stu2.learn('语文')  # alex is learning 语文

# 类中定义的函数属性, 在未经处理的情况下, 不是给类用的, 是绑定给对象使用的.
# 如果对象中和类中的属性存在重名, 则优先查找对象自己中的, 没有则去类中找, 自己的类中没有则去父类中去找, 都找不到会报错, 不会去全局中找.

1.10 python中一切皆对象

  1. 站在不同的角度, 定义出来的类是截然不同的

  2. 现实中的类不完全等于程序中的类

  3. 有时为了变成需要, 程序中可能会出现现实中不存在的类

# python中一切皆对象, python3中统一了类与类型的概念, 类型就是类.
print(type([1,2]))  # <class 'list'>
l = [1, 2, 3]  # l = list([1, 2, 3]), 类list实例化对象
l.append(5)  #l对象调用绑定方法append

1.11 面向对象可扩展性总结

1.12 小练习1

'''
练习1:
编写一个学生类, 产生一堆学生对象
要求:
有一个计数器(属性), 统计总共实例了多少对象
'''
class Student:
    school = 'luffycity'
    count = 0
    def __init__(self, name, age, gender):
        self.name = name
        self.age = age
        self.gender = gender
        # self.count += 1  会为对象创建私有属性count
        Student.count+=1
    def learn(self):
        print('%s is learning' %self.name)
stu1 = Student('egon', 'male', 18)
stu2 = Student('alex', 'female', 28)
print(Student.count)  # 2
print(stu1.count)  # 2
print(stu2.count)  # 2

1.13 小练习2

'''
练习2:
模仿王者荣耀定义两个英雄类
要求:
英雄需要有昵称, 攻击力, 生命值等属性
实例化出两个英雄对象
英雄之间可以互殴, 被殴打的一方掉血, 血量小于0则判定为死亡.
'''
class Garen:
    camp = 'Demacia'
    def __init__(self, nickname, life_value, aggresivity):
        self.nickname = nickname
        self.life_value = life_value
        self.aggresivity = aggresivity
    def attack(self, enemy):
        enemy.life_value -= self.aggresivity
class Riven:
    camp = 'Noxus'
    def __init__(self, nickname, life_value, aggresivity):
        self.nickname = nickname
        self.life_value = life_value
        self.aggresivity = aggresivity
    def attack(self, enemy):
        enemy.life_value -= self.aggresivity
g1 = Garen('草丛伦', 100, 30)
r1 = Riven('瑞文', 80, 50)
g1.attack(r1)
print(r1.life_value)  # 50

1.14 继承与重用性

'''
什么是继承?
继承指的是类与类之间的关系, 是一种'什么是什么的关系', 继承的功能之一就是用来解决代码重用问题.
继承是一种创建新类的方式, 在python中, 新建的类可以继承一个或多个父类, 父类又可以称为基类或超类, 新建的类称为派生类或子类.
'''
class ParentClass1:
    pass
class ParentClass2:
    pass
class SubClass1(ParentClass1):
    pass
class SubClass2(ParentClass1, ParentClass2):
    pass
print(SubClass1.__bases__)  # 查看父类, (<class '__main__.ParentClass1'>,)
print(SubClass2.__bases__)  # (<class '__main__.ParentClass1'>, <class '__main__.ParentClass2'>)


class Hero:
    def __init__(self, nickname, life_value, aggresivity):
        self.nickname = nickname
        self.life_value = life_value
        self.aggresivity = aggresivity
    def attack(self, enemy):
        enemy.life_value -= self.aggresivity
class Garen(Hero):
    camp = 'Demacia'
class Riven(Hero):
    camp = 'Noxus'
g1 = Garen('草丛伦', 100, 30)
r1 = Riven('瑞文', 80, 50)
g1.attack(r1)
print(g1.nickname)  # 从对象自己这找, 如果没有则去类中找, 如果没有则去父类中找, 否则报错, 不会去全局找.
print(r1.life_value)  # 50

# 属性查找小练习
class Foo:
    def f1(self):
        print('from Foo.f1')
    def f2(self):
        print('from FOO.f2')
        self.f1()  # b.f1()
class Bar(Foo):
    def f1(self):
        print('from Bar.f1')
b = Bar()
b.f2()
'''
结果:
from FOO.f2
from Bar.f1
'''

 1.15 派生

# 如果子类中派生出新的属性, 则以新的属性为主
class Hero:
    def __init__(self, nickname, life_value, aggresivity):
        self.nickname = nickname
        self.life_value = life_value
        self.aggresivity = aggresivity
    def attack(self, enemy):
        enemy.life_value -= self.aggresivity
class Garen(Hero):
    camp = 'Demacia'
    def attack(self, enemy):
        print('from Garen class')
class Riven(Hero):
    camp = 'Noxus'
g1 = Garen('草丛伦', 100, 30)
r1 = Riven('瑞文', 80, 50)
g1.attack(r1)
print(g1.nickname)  # 从对象自己这找, 如果没有则去类中找, 如果没有则去父类中找, 否则报错, 不会去全局找.
print(r1.life_value)  # 80

1.16 继承的实现原理

python到底是如何实现继承的? 对于定义的每一个类, python会计算出一个方法解析顺序(MRO)列表, 该列表是一个简单的所有基类的线性排序列表, 例如:

>>> F.mro() #等同于F.__mro__
[<class '__main__.F'>, <class '__main__.D'>, <class '__main__.B'>, 
<class '__main__.E'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]

为了实现继承, python会在MRO列表上从左到右开始查找基类, 查找到第一个匹配这个属性的类为止, 而这个MRO列表的构造是通过一个C3线性算法来实现的. 它实际上就是合并所有父类的MRO列表并遵循如下三条准则:

  1. 子类会先于父类被检查

  2. 多个父类会根据它们在列表中的顺序被检查

  3. 如果对下一个类存在两个合法的选择, 选择第一个父类.

在Java和C#中子类只能继承一个父类,而Python中子类可以同时继承多个父类,如果继承了多个父类,那么属性的查找方式有两种,分别是:深度优先和广度优先

python中类分为两种: 新式类和经典类, python2中有这两种分法, python3中只有新式类.

python2中的经典类: 没有继承object的类, 以及它的子类都称为经典类.

python2中的新式类: 继承了object的类, 以及它的子类都称为新式类.

python3中默认继承object类, 都是新式类

class Foo:
    pass
print(Foo.__bases__)  # (<class 'object'>,)

class Foo:
    pass
print(Foo.__bases__)  # (<class 'object'>,)

# 验证多继承情况下的属性查找
class A(object):
    def test(self):
        print('from A')

class B(A):
    def test(self):
        print('from B')

class C(A):
    def test(self):
        print('from C')

class D(B):
    def test(self):
        print('from D')

class E(C):
    def test(self):
        print('from E')

class F(D,E):
    # def test(self):
    #     print('from F')
    pass
f1=F()
f1.test()
print(F.__mro__) #只有新式才有这个属性可以查看线性列表,经典类没有这个属性
'''
结果:
(<class '__main__.F'>, <class '__main__.D'>, <class '__main__.B'>,
 <class '__main__.E'>, <class '__main__.C'>, <class '__main__.A'>, 
 <class 'object'>)
'''

#新式类继承顺序:F->D->B->E->C->A
#经典类继承顺序:F->D->B->A->E->C
#python3中统一都是新式类
#pyhon2中才分新式类与经典类

 

1.17 在子类中重用父类的方法或属性

# 在子类派生出的新的方法中重用父类的方法, 有两种实现方式:
# 方式一: 指名道姓(不依赖继承)
# 方式二: super()(依赖继承), super会在MRO列表中寻找关系
class Hero:
    def __init__(self, nickname, life_value, aggresivity):
        self.nickname = nickname
        self.life_value = life_value
        self.aggresivity = aggresivity
    def attack(self, enemy):
        enemy.life_value -= self.aggresivity
class Garen(Hero):
    camp = 'Demacia'
    def __init__(self, nickname, life_value, aggresivity, weapon):
        # Hero.__init__(self, nickname, life_value, aggresivity)   # 指明道姓的方式
        super(Garen, self).__init__(nickname, life_value, aggresivity)
        # super(Garen, self)创建了一个对象, python2中需要这么写, 但是在python3中可以简写成以下方式:
        # super().__init__(nickname, life_value, aggresivity)
        self.weapon = weapon
    def attack(self, enemy):
        # Hero.attack(self, enemy)  # 指明道姓的方式, 不依赖于继承关系
        super(Garen, self).attack(enemy)
        print('from Garen class')
class Riven(Hero):
    camp = 'Noxus'
g1 = Garen('草丛伦', 100, 30, '金箍棒')
r1 = Riven('瑞文', 80, 50)
g1.attack(r1)
print(r1.life_value)  # 50

class A:
    def f1(self):
        print('from A')
        super().f1()
class B:
    def f1(self):
        print('from B')
class C(A, B):
    pass
print(C.mro())
# [<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>]
c = C()
c.f1()
'''
结果:
from A
from B
'''
# 说明, 在查找时基于C的MRO列表进行查找.

 

1.18 组合

# 组合: 解决谁有谁的类之间的关系.
class Person:
    school = 'luffycity'
    def __init__(self, name, age, gender):
        self.name = name
        self.age = age
        self.gender = gender
class Teacher(Person):
    school = 'luffycity'
    def __init__(self, name, age, gender, level, salary):
        super().__init__(name, age, gender)
        self.level = level
        self.salary = salary
    def teach(self):
        print('%s is teaching' %self.name)
class Student(Person):
    school = 'luffycity'
    def __init__(self, name, age, gender, clas):
        super().__init__(name, age, gender)
        self.clas = clas
    def teach(self):
        print('%s is teaching' %self.name)
class Course:
    def __init__(self, cname, price, period):
        self.cname = cname
        self.price = price
        self.period = period
    def tell_info(self):
        print('课程名<%s>'%self.cname)

t1 = Teacher('alex', 18, 'female', 10, 3000)
s1 = Student('egon', 28, 'female', '一班')
c1 = Course('语文', 100, 20)
c2 = Course('数学', 200, 50)
t1.course1 = c1  # t1的属性指向了一个对象
print(t1.course1.cname)
t1.course1.tell_info()

t1.course2 = c2  # t1的属性指向了一个对象
print(t1.course2.cname)
t1.course2.tell_info()

s1.courses = []
s1.courses.append(c1)
s1.courses.append(c2)

 

1.19 抽象类与归一化

import abc
class Animal(metaclass=abc.ABCMeta):  # 抽象类, 只能被继承, 不能被实例化. 其作用是规范子类.
    all_type = 'animal'
    @abc.abstractmethod
    def run(self):
        pass
    @abc.abstractmethod
    def eat(self):
        pass
class People(Animal):
    def run(self):  # 必须包含父类中的run和eat方法.
        print('People is running')
    def eat(self):
        print('People is eating')
class Pig(Animal):
    def run(self):
        print('Pig is running')
    def eat(self):
        print('Pig is eating')
peop1 = People()
pig1 = Pig()
peop1.run()  # People is running
pig1.run()  # Pig is running

1.20 多态与多态性

# 多态:  同一类事物的多种形态
import abc
class Animal(metaclass=abc.ABCMeta):
    all_type = 'animal'
    @abc.abstractmethod
    def run(self):
        pass
class People(Animal):
    def run(self):
        print('People is running')
class Pig(Animal):
    def run(self):
        print('Pig is running')
# 多态性: 指的是可以在不考虑对象类型的情况下直接使用对象
# peop1, pig1都是动物, 只要是动物肯定有run方法, 于是我们可以不用考虑它们具体是什么类型, 可以直接使用
peop1 = People()
pig1 = Pig()
peop1.run()  # People is running
pig1.run()  # Pig is running
# 以上称为动态多态性
# 静态多态性: 如任何类型都可以使用运算符+进行运算
# 更进一步, 我们可以定义一个统一的接口进行使用
def func(animal):
    animal.run()
func(peop1)
func(pig1)
# 以上示例中继承了同一父类, 子类受父类的约束
'''
多态性的好处:
1. 增加了程序的灵活性
    以不变应万变, 不论对象千变万化, 使用者都是同一形式去调用
2. 增加了程序的可扩展性
'''

class File:
    def read(self):
        print('file read')
    def write(self):
        print('file write')
class Disk:
    def read(self):
        print('disk read')
    def write(self):
        print('disk write')
class Text:
    def read(self):
        print('text read')
    def write(self):
        print('text write')
f = File()
d = Disk()
t = Text()
def f1(x):
    x.read()
    x.write()
f1(f)
f1(d)
f1(t)
# 以上示例没有继承同一父类, 但是彼此存在相似之处.

'''
python崇尚鸭子类型, 只要长得像鸭子, 就是鸭子.
类与类之间不继承同一父类, 只要有相似之处, 就可以了.
'''

 1.21 封装之如何隐藏属性

class A:
    __x = 1  # 变形隐藏  _A__x = 1
    def __init__(self, name):
        self.__name = name
    def __foo(self):  # 定义阶段, 检测语法时转化: def _A__foo(self):
        print('run foo')
    def bar(self):
        self.__foo()  # self._A__foo()
        print('from bar')
# print(A.__x)  # AttributeError: type object 'A' has no attribute '__x', 说明已经隐藏了
a = A('egon')
print(A.__dict__)
print(a.__dict__)  # {'_A__name': 'egon'}
a.bar()  # run foo  from bar
a._A__foo()  # run foo

'''
这种变形的特点:
    1. 在类外部无法直接通过obj.__AttriName访问
    2. 在类内部可以直接通过obj.__AttriName访问
    3. 子类无法覆盖父类__开头的属性
'''

class Foo:
    def __func(self):  # _Foo__func
        print('from foo')
class Bar(Foo):
    def __func(self):  # _Bar__func
        print('from bar')
# 加__会在类的定义阶段进行变形

'''
总结这种变形需要注意的问题:
1. 这种机制并没有真正意义上限制我们从外部直接访问属性, 知道了类名和属性名就可以拼出名字:
_类名__属性名,然后就可以访问了.
2. 变形的过程只在类的定义阶段发生一次, 在定义后的赋值操作, 不会变形.
3. 在继承中, 父类如果不想让子类覆盖自己的方法, 可以将方法定义为私有
'''
# 验证问题2
class B:
    __x = 1
    def __init__(self, name):
        self.__name = name
B.__y = 2
print(B.__dict__) # {..., '__y': 2}
# 验证问题3
class C:
    def foo(self):
        print('C.foo')
    def bar(self):
        print('C.bar')
        self.foo()
class D(C):
    def foo(self):
        print('D.foo')
d = D()
d.bar()  #C.bar  D.foo

class C:
    def __foo(self):  # _C__foo
        print('C.foo')
    def bar(self):
        print('C.bar')
        self.__foo()  # self._C__foo
class D(C):
    def __foo(self):  # _D__foo
        print('D.foo')
d = D()
d.bar()  #  C.bar  C.foo

 1.22 封装的意义

# 一, 封装数据属性的目的: 明确地区分内外, 控制外部对隐藏属性的操作行为.
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 not isinstance(name,str):
            print('名字必须是字符串类型')
            return
        if not isinstance(age, int):
            print('年龄必须是数字类型')
            return
        self.__name = name
        self.__age = age
p = People('egon', 18)
p.tell_info()
# 加了__后则无法直接访问或修改属性, 必须通过特定的接口(如tell_info, set_info)来间接进行访问和修改.
# 接口上附加一些逻辑, 来控制外部对隐藏属性的访问和修改.

# 二, 封装方法的目的: 隔离复杂度
# 不让直接接触到零散的方法, 如__card, __auth, 可以直接接触到封装好的方法, 如withdraw.
class ATM:
    def __card(self):
        print('插卡')
    def __auth(self):
        print('用户认证')
    def __input(self):
        print('输入取款金额')
    def __print(self):
        print('打印账单')
    def __take_money(self):
        print('取款')
    def withdraw(self):
        self.__card()
        self.__auth()
        self.__input()
        self.__print()
        self.__take_money()
a = ATM()
a.withdraw()

1.23 封装与可扩展性

class Room:
    def __init__(self, name, owner, height, weight, length):
        self.name = name
        self.owner = owner
        self.__height = height
        self.__weight = weight
        self.__length = length
    def tell_area(self):
        return self.__weight * self.__length
r=Room('卫生间','alex',10,10,10)
print(r.tell_area())
# 使用者只需要记住接口即可, 无需关注里面的原理.

1.24 property的使用

class People:
    def __init__(self, name, weight, height):
        self.name = name
        self.weight = weight
        self.height = height
    @property  # 将bmi的调用方式由bmi()转为bmi, 使使用者感觉调用的不是一个功能, 而是一个属性.
    def bmi(self):
        return self.weight / (self.height ** 2)
p = People('egon', 75, 1.81)
print(p.bmi)
# 注意: bmi本质是一个方法, 不能对其进行赋值.

class People:
    def __init__(self, name):
        self.__name = name
    @property
    def name(self):
        return self.__name
# 注意: name本质是一个方法, 不能对其进行赋值.
# 不过, 通过以下方法可以实现赋值.
    @name.setter
    def name(self, val):
        if not isinstance(val, str):
            print('名字必须是字符串类型')
        self.__name = val
    @name.deleter
    def name(self):
        print('不允许删除')
p = People('egon')
p.name = 'EGON'
print(p.name)  # EGON
del p.name  # 不允许删除

1.25 绑定方法与非绑定方法介绍

'''
在类内部定义的函数, 分为两大类:
    一: 绑定方法: 绑定给谁, 就应该由谁来调用, 谁来调用就自动把谁当作第一个参数传入.
        绑定到对象的方法: 在类内定义的没有被任何装饰器修饰的方法
        绑定到类的方法: 在类内定义的被装饰器classmethod修饰的方法
    二: 非绑定方法: 没有自动传值这么一说, 就是在类中定义的一个普通工具, 对象和类都可以使用.
        非绑定方法: 不与类或者对象绑定
'''
class Foo:
    def __init__(self, name):
        self.name = name
    def tell(self):  # 绑定到对象的方法
        print('名字是%s' %self.name)
    @classmethod
    def func(cls):  # 绑定到类的方法
        print(cls)
    @staticmethod
    def func1(x, y):  #括号里面可以写参数, 也可以不写参数. 没有自动传递的参数.
        print(x+y)

f = Foo('egon')
print(Foo.tell)  # <function Foo.tell at 0x0000026860001A60>
Foo.tell(f) # 此处, 必须对self进行传值, self不能自动传值.
print(f.tell)  # <bound method Foo.tell of <__main__.Foo object at 0x000002B04F8B3CC0>>
f.tell()

print(Foo.func)  # <bound method Foo.func of <class '__main__.Foo'>>
Foo.func()  # <class '__main__.Foo'>

print(Foo.func1)  # <function Foo.func1 at 0x000002BF6F551BF8>
print(f.func1)  # <function Foo.func1 at 0x000002BF6F551BF8>
Foo.func1(1,2)  # 3
f.func1(1,3)  # 4

1.26 绑定方法与非绑定方法的使用

import settings
import hashlib
import time
class People:
    def __init__(self, name, age, gender):
        self.id = self.create_id()
        self.name = name
        self.age = age
        self.gender = gender
    def tell_info(self):  # 绑定到对象的方法
        print('Name: %s, Age:%s, Gender:%s'%(self.name, self.age, self.gender))
    @classmethod
    def from_conf(cls):
        obj = cls(settings.name, settings.age, settings.gender)
        return obj
    @staticmethod
    def create_id():
        time.sleep(0.01)
        m = hashlib.md5(str(time.time()).encode('utf-8'))
        return m.hexdigest()

p1 = People('egon', 18, 'male')
# 绑定给对象, 就应该由对象来调用, 自动将对象本身当作第一个参数传入
p1.tell_info()  # Name: egon, Age:18, Gender:male

# 绑定给类, 就应该由类来调用, 自动将类本身当作第一个参数传入
p2 = People.from_conf()
p2.tell_info()  # Name: alex, Age:10, Gender:male

# 非绑定方法, 不与类或者对象绑定, 谁都可以来调用, 没有自动传值一说.
p3 = People('aaa', 12, 'female')
p4 = People('bbb', 22, 'male')
p5 = People('ccc', 18, 'female')
print(p3.id)  # e4487e9a7fed738946cd85e9d38bd795
print(p4.id)  # b02113fa5ebd6a9e1c50b25c2e7c48f3
print(p5.id)  # 8f157e0fd5d76047055fc5f77fc90b98
# settings.py
name = 'alex'
age = 10
gender = 'male'

1.27 反射

# 反射: 通过字符串映射到对象的属性
class People:
    country = 'china'
    def __init__(self, name, age):
        self.name = name
        self.age = age
    def talk(self):
        print('%s is talking' %self.name)
obj = People('egon', 18)

# 1. hasattr
print(hasattr(obj,'name'))  # True, 判断obj下是否有name属性
print(hasattr(obj,'talk'))  # True, 判断obj下是否有talk
# 2. getattr
print(getattr(obj, 'name'))  # egon  有则返回结果, 没有则报错, 如果不想让其报错, 可以使用default
print(getattr(obj, 'namex', None))  # None,  没有返回None
# 3. setattr
setattr(obj, 'age', 50)
print(obj.age)  # 50
setattr(obj, 'gender', 'male')
print(obj.__dict__)  # {'name': 'egon', 'age': 50, 'gender': 'male'}
print(obj.gender)  # male
# 4. delattr
delattr(obj, 'gender')
print(obj.__dict__)  # {'name': 'egon', 'age': 50}
# 以上方法也适用于类
print(getattr(People, 'country'))  # china

# 反射的应用:
class Service:
    def run(self):
        while True:
            inp = input('>>>:').strip()
            cmds = inp.split()
            if hasattr(self, cmds[0]):
                func=getattr(self, cmds[0])
                func(cmds)
    def get(self, cmds):
        print('get...', cmds)
    def put(self, cmds):
        print('put...', cmds)
obj = Service()
# 接收用户输入并触发后面的方法
obj.run()
'''
结果:
>>>:get
get... ['get']
>>>:get a.txt
get... ['get', 'a.txt']
'''

1.28 内置方法介绍

https://www.cnblogs.com/linhaifeng/articles/6204014.html

一: isinstance(obj,cls)和issubclass(sub,super)

isinstance(obj,cls)检查是否obj是否是类 cls 的对象

1 class Foo(object):
2     pass
3  
4 obj = Foo()
5  
6 isinstance(obj, Foo)

issubclass(sub, super)检查sub类是否是 super 类的派生类

1 class Foo(object):
2     pass
3  
4 class Bar(Foo):
5     pass
6  
7 issubclass(Bar, Foo)

二: item系列

# item系列
# 将对象模拟地像字典
class Foo:
    def __init__(self, name):
        self.name = name
    def __getitem__(self, item):
        return self.__dict__.get(item)
    def __setitem__(self, key, value):
        self.__dict__[key] = value
    def __delitem__(self, key):
        # del self.__dict__[key]
        self.__dict__.pop(key)
obj = Foo('egon')
# 查看属性
print(obj['name'])
# 设置属性
obj['gender'] = 'male'
# 删除属性
del obj['name']
# __str__方法:
class People:
    def __init__(self, name, age):
        self.name = name
        self.age = age
    def __str__(self):
        return '<name:%s, age:%s>'%(self.name, self.age)
obj = People('egon', 18)
print(obj)  # 打印对象时触发对象下的__str__方法, 并且要求返回的一定是字符串类型的数据
# __del__方法
# 在python自动回收对象之前触发__del__, 回收跟对象相关联的某些资源.
class Open:
    def __init__(self, filename):
        print('open file...')
        self.filename = filename
    def __del__(self):
        print('回收操作系统等资源: f.close()')
f = Open('settings.py')
print('--------main---------')
# del f  会手动触发__del__, 不使用这种方法则自动调用__del__
'''
结果:
open file...
--------main---------
del...
'''

 1.29 元类

https://www.cnblogs.com/linhaifeng/articles/8029564.html

# 元类介绍
'''
储备知识exec命令的使用, 有三个参数
参数1: 字符串形式的命令
参数2: 全局作用局(字典形式), 如果不指定就默认使用globals()
参数3: 局部作用局(字典形式), 如果不指定就默认使用locals()
'''
g = {
    'x':1,
    'y':2
}
l = {}
exec('''
global x,m
x = 10
m = 100
z = 3
''', g, l)
print(g)
# 可以将exec当作函数看待

'''
python中一切皆对象, 对象可以怎么用?
1. 都可以被引用, x = obj
2. 都可以当作函数的参数传入
3. 都可以当作函数的返回值
4. 都可以当作容器类型的元素
'''
# 类也是对象, 对象是经过实例化类得来的, 那么Foo也应该是由类实例化得到的.
class Foo:  # Foo = type(...)
    pass
obj = Foo()
print(type(obj))  # <class '__main__.Foo'>
print(type(Foo))  # <class 'type'>

#产生类的类称之为元类, 默认所有用class定义的类, 它们的元类是type.
# 定义类的两种方式:
# 方式一: class
class Chinese:  # Chinese  = type(...)
    country = 'china'
    def __init__(self, name, age):
        self.name = name
        self.age = age
    def talk(self):
        print('%s is talking' %self.name)
# 方式二:
# 定义类的三要素: 类名, 类的基类们, 类的名称空间
class_name = 'Chinese'
class_bases = (object,)
class_body = '''
country = 'china'
def __init__(self, name, age):
    self.name = name
    self.age = age
def talk(self):
    print('%s is talking' %self.name)
'''
class_dic = {}
exec(class_body,globals(),class_dic)
print(class_dic)  # {'country': 'china', '__init__': <function __init__ at 0x0000022563A21EA0>, 'talk': <function talk at 0x0000022563A380D0>}
Chinese1 = type(class_name,class_bases,class_dic)
print(Chinese1)  # <class '__main__.Chinese'>
obj1 = Chinese1('alex', 20)
print(obj1, obj1.name, obj1.age)  # <__main__.Chinese object at 0x0000018894F4FF98> alex 20
# 定义元类控制类的创建
class Mymeta(type):
    def __init__(self, class_name, class_bases, class_dic):
        if not class_name.istitle():
            raise TypeError('类名的首字母必须大写')
        if '__doc__' not in class_dic or not class_dic['__doc__'].strip():
            raise TypeError('必须有注释, 且注释不能为空')
        super(Mymeta, self).__init__(class_name, class_bases, class_dic)

class Chinese(object,metaclass=Mymeta):  # Chinese = Mymeta(class_name, class_bases, class_dic)
    ''''''
    country = 'china'
    def __init__(self, name, age):
        self.name = name
        self.age = age
    def talk(self):
        print('%s is talking' %self.name)
# 自定义元类控制类的实例化行为
# 知识储备__call__方法
class Foo:
    def __call__(self, *args, **kwargs):
        print(args, kwargs)
obj = Foo()
obj(1, 2, x = 'aa')  # (1, 2) {'x': 'aa'}, 在调用对象时会自动触发执行__call__方法
# 元类内部也应该有一个__call__方法, 会在调用Foo时触发执行.

class Mymeta(type):
    def __init__(self, class_name, class_bases, class_dic):
        if not class_name.istitle():
            raise TypeError('类名的首字母必须大写')
        if '__doc__' not in class_dic or not class_dic['__doc__'].strip():
            raise TypeError('必须有注释, 且注释不能为空')
        super(Mymeta, self).__init__(class_name, class_bases, class_dic)
    def __call__(self, *args, **kwargs):
        # 第一件事: 先造出一个空对象obj
        obj = object.__new__(self)
        # 第二件事: 初始化obj
        self.__init__(obj,*args,**kwargs)
        # 第三件事: 返回obj
        return obj

class Chinese(object,metaclass=Mymeta):  # Chinese = Mymeta(class_name, class_bases, class_dic)
    '''
    xxx
    '''
    country = 'china'
    def __init__(self, name, age):
        self.name = name
        self.age = age
    def talk(self):
        print('%s is talking' %self.name)
obj = Chinese('egon', age=18)
print(obj.__dict__)
# 自定义元类控制类的实例化行为的应用
# 单例模式
# 实现方式一:
class MySQL:
    __instance = None
    def __init__(self):
        self.host = '127.0.0.1'
        self.port = 3306
    @classmethod
    def singleton(cls):
        if not cls.__instance:
            obj = cls()
            cls.__instance=obj
        return cls.__instance
obj1 = MySQL.singleton()
obj2 = MySQL.singleton()

# 实现方式二: 元类的方式
class Mymeta(type):
    def __init__(self, class_name, class_bases, class_dic):
        if not class_name.istitle():
            raise TypeError('类名的首字母必须大写')
        if '__doc__' not in class_dic or not class_dic['__doc__'].strip():
            raise TypeError('必须有注释, 且注释不能为空')
        super(Mymeta, self).__init__(class_name, class_bases, class_dic)
        self.__instance = None
    def __call__(self, *args, **kwargs):
        if not self.__instance:
            obj = object.__new__(self)
            self.__init__(obj)
            self.__instance = obj
        return self.__instance
class Mysql(object,metaclass=Mymeta):
    '''
    xxx
    '''
    def __init__(self):
        self.host = '127.0.0.1'
        self.port = 3306
obj1 = Mysql()
obj2 = Mysql()
obj3 = Mysql()
print(obj1 is obj2 is obj3)  # True

 

面向对象实战:

https://www.cnblogs.com/linhaifeng/articles/6182264.html#_label15

posted @ 2020-08-03 09:45  自由者妍  阅读(206)  评论(0)    收藏  举报