Python


一.面向对象的初始

​ 1.类 : 具有相同属性和技能的一类事物

​ 2.对象 : 具体类的表现, 具体的实实在在的一个实例

class Person:
    ''' 类体:两部分: 变量部分,方法(函数)部分 '''
    mind = '有思想'  # 变量,一般叫(静态变量,静态字段)
    animal = '高级动物'
    faith = '有信仰'

    def __init__(self):
        print(66)

    def work(self):  # 方法,函数,动态变量
        print("人类都会工作")

    def shop(self):
        print("人类可以消费....")
        self.hight = 175 # 方法中也可以封装属性

# 类名的角度
    # 类中的静态变量
        # 1. Person.__dict__ 查询类中的所有内容,不能进行增删改查

# print(Person.__dict__)
# print(Person.__dict__['faith']) # 获取类中静态字段具体的值
# Person.__dict__['mind'] = '无脑' # 报错
# print(Person.__dict__['mind'])

        # 2. 万能的点 . 对类中的单个的变量进行增删改查

# print(Person.mind)  # 查
# print(Person.animal) # 查

# Person.money = '运用货币' 增
# Person.mind = '无脑' # 改
# del Person.mind  # 删除
# print(Person.__dict__) # 打印类中所有的内容

        # 3. 操作类中的方法 (工作中基本不用类名去操作)
        # 3. 除了类方法,静态方法,需要类名调用之外,剩下的方法都要对象调用
# Person.work(11)

# 对象的角度
# ret = Person() # 类名 + () 的这个过程: 实例化的过程( 创建一个对象的过程 )
    # Person() 实例化对象,实例,对象
# print(ret)
#  1. 只要类名 + () 产生一个对象,自动执行类中__init__方法.

执行步骤
# 1. 类名+() 产生一个实例 ( 对象,对象空间 )
# 2. 自动执行类中的__init__方法,将对象空间传给__init__的self参数
# 3. 给对象封装相应的属性(变量)
# print(ret.__dict__) # 查询对象中的所有内容 结果是 字典{}

# 执行过程
# 1.创建了一个对象空间,实例空间
# 2. 自动执行__init__方法,并将我的对象空间传给self
# 3. 执行具体的__init__代码,给对象空间封装属性

# 操作对象中静态变量
    # 1. __dict__ 查询对象中的所有内容
    # 2. 万能的点 . 
print(ret.name)  # 查
ret.high = 189  # 增
del ret.faith  # 删除
ret.age = 99  # 改

# print(ret.__dict__)  # 

对象操作类中的静态变量 : 只能查询
# print(ret.mind)

对象调用类中的方法 ( 工作中 通过对象执行类中的方法,而不是通过类名)
# ret.shop()
# print(ret)

例子

class Person:
    mind = '有思想的'
    animal = '高级动物'
    faith = '有信仰的'

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

    def work(self):
        print('%s是会工作的' % self.name)

    def shop(self):
        print('%s会消费......' % self.name)

    def get(self):
        print('%s今年%s岁了' % (self.name, self.age))


ret = Person('alex', 19, '喝酒')
# ret.work()
# ret.shop()
# ret.get()

二.面向对象-名称空间

1.创建一个类就会创建一个类的名称空间,用来存储类中定义的所有名字,这些名字称为类的属性

​ 而类有两种属性: 静态属性和动态属性

​ 静态属性就是直接在类中定义的变量

​ 动态属性就是定义在类中的方法

​ 其中类的数据属性是共享给所有对象的

>>>id(egg.role)
4341594072 # 内存地址
>>>id(Person.role)
4341594072

​ 而类动态属性是绑定到所有对象的

>>>egg.attack
<bound method Person.attack of <__main__.Person object at 0x101285860>>
>>>Person.attack
<function Person.attack at 0x10127abf8> 

创建一个对象/实例就会创建一个对象/实例的名称空间, 存放对象/实例的名字. 称为对象/实例的属性

在obj.name 会先从obj 自己的名称空间找 name .找不到则去类中找,再找不到就去父类中扎找…最好找不到就会抛出异常

  1. 查询顺序:
    1. 对象.属性: 先从对象空间找.如果找不到.再从类空间找.再找不到.再从父类中找..
    2. 类名.属性: 先从本类空间中找.如果找不到,在从父类中找..
  2. 对象与对象之间是相互独立的
计算一个类 实例化多少对象.

class Count:
    count = 0
    def __init__(self):
        Count.count = self.count + 1

obj1 = Count()
obj2 = Count()

print(Count.count)
count = 0

def func():
    print(count)
func()

class Count:
    count = 0
    def __init__(self):
        pass

通过类名可以更改我的类中的静态变量值
Count.count = 6
print(Count.__dict__)

但是通过对象 不能改变只能引用类中的静态变量

obj1 = Count()
print(obj1.count)
obj1.count = 6

三.组合: 给一个类的对象封装一个属性,这个属性是另一个类的对象

class GameRole:
    def __init__(self, name, ad, hp):
        self.name = name
        self.ad = ad
        self.hp = hp

    def attack(self,p):
        p.hp = p.hp - self.ad
        print('%s 攻击 %s,%s 掉了%s血,还剩%s血' %(self.name,p.name,p.name,self.ad,p.hp))

    def armament_weapon(self,wea):
        self.wea = wea


class Weapon:
    def __init__(self,name,ad):
        self.name = name
        self.ad = ad


    def fight(self,p1,p2):
        p2.hp = p2.hp - self.ad
        print('%s 用%s打了%s,%s 掉了%s血,还剩%s血'\
              % (p1.name,self.name,p2.name,p2.name,self.ad,p2.hp))

p1 = GameRole('大阳哥',20,500)
p2 = GameRole('印度阿宁',50,200)
axe = Weapon('三板斧',60)
broadsword = Weapon('屠龙宝刀',100)
# print(axe)
p1.armament_weapon(axe)  # 给大阳哥 装备了三板斧这个对象.
# print(p1.wea)
# print(p1.wea.name)
# print(p1.wea.ad)
p1.wea.fight(p1,p2)

四.面向对象第一大特性—继承

  1. 子类以及子类实例化的对象, 可以访问父类的任何方法和变量

  2. 类名可以访问父类的所有内容 (print(Animal.breath))

  3. 子类实例化的对象也可以访问父类所有内容 print(p1.breath) print(p1) p1.eat()

  4. 只执行父类的方法: 子类中不要定义与父类同名的方法

  5. 只执行子类的方法,在子类中创建

  6. 既要执行子类的方法,又要执行父类中的方法?

    1. 两种办法:

      Animal.__init__(self, name, sex, age)
      super().__init__(name,sex,age)
class Animal:
    breath = '呼吸' # 静态字段

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

    def eat(self):
        print('吃东西...')

class Cat(Animal): # 括号里面的: 父类,基类,超类 括号外面的: 子类,派生类
    pass

class Dog(Animal):
    pass

p1 = Cat('alex','women',100)    # 实例化子类对象
print(p1.__dict__)
class Animal:

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

    def eat(self, a):
        print('%s吃%s' % (self.name, a))

    def drink(self):
        print('%s喝东西' % self.name)


class Cat(Animal):
    def __init__(self, name, sex, age, wing):
        Animal.__init__(self, name, sex, age)
        # super(Cat, self).__init__(name, sex, age)
        self.wing = wing

    def miaow(self):
        print('喵喵叫')

    def eat(self, a):
        super(Cat, self).eat(a)
        print(666)


c1 = Cat('tom', '公', 3, '好看')
# print(c1.__dict__)
c1.eat('金蝉')

五.继承的进阶

​ 1.继承: 单继承, 多继承

​ 2.类的角度: A–>经典类,B–>新式类

​ 1.新式类: 凡是继承object类都是新式类( 在Python3版本中,所有类都是新式类,因为Python3版本中的类都默认继承object)

​ 2.经典类: 不继承object类的都是经典类(Python2中既有新式类又有经典类,中所有的类都不继承object,所以所有的类默认是经典类)

​ 3.单继承: 新式类,经典类查询顺序一样

class A:
    def func(self):
        print('A')


class B(A):
    def func(self):
        print('B')


class C(B):
    def func(self):
        print('C')


c1 = C()
c1.func()
b1 = B()
b1.func()

​ 4.多继承:

​ 1.新式类: 遵循广度优先

​ 2.多继承的新式类 广度优先: 一条路都到倒数第二级,进行判断,如果其他路能走到终点,则返回走另外一条路,如果不能,则走到终点

1603693720711

class A:
    def func(self):
        print('IN A')


class B(A):
    pass
    # def func(self):
    #     print('IN B')


class C(A):
    pass
    # def func(self):
    #     print('IN C')


class D(B):
    pass
    # def func(self):
    #     print('IN D')


class E(C):
    pass
    # def func(self):
    #     print('IN E')


class F(D, E):
    pass
    # def func(self):
    #     print('IN F')

f1 = F()
f1.func()

print(F.mro())  # 查询类的继承顺序

​ 3.经典类: 遵循深度优先: 一条路走到底

六.面向对象之封装和多态

​ 一. 接口类,抽象类: 制定一个规范(Python中没有接口的概念)

# 制定规则,抽象类,接口类
from abc import ABCMeta, abstractmethod


class Payment(metaclass=ABCMeta):  # 抽象类(接口类)
    @abstractmethod  # 装饰器
    def pay(self): pass  # 制定规范


class Alipay(Payment):
    def __init__(self, money):
        self.money = money

    def pay(self):
        print('使用了支付宝支付了%s' % self.money)


class Jdpay(Payment):
    def __init__(self, money):
        self.money = money

    def pay(self):
        print('使用了京东支付了%s' % self.money)


def pay(obj):
    obj.pay()


a1 = Alipay(1000)
p1 = Jdpay(1222)

pay(a1)  # 归一化设计
pay(p1)

​ 二. 多态

​ 1.多态: Python中没有多态,只有鸭子类型(Python是弱类型语言,不管是什么类型,传入函数,封装到对象中都可以)

​ 以下这些类互称为鸭子

class Str:
    def index(self):
        pass


class List:
    def index(self):
        pass


class Tuple:
    def index(self):
        pass

​ 三.封装

​ 1.广义的封装: 实例化一个对象, 给对象空间封装一些属性

​ 2.狭义的封装: 私有制

​ 3.私有成员: 私有静态字段,私有方法,私有对象属性

​ A.私有静态字段

class A:
    __money = 1100  # 前面加 __就是私有静态字段


class B(A):
    name = 'alex'
    __age = 99  # 前面加 __就是私有静态字段

    def func(self):
        print(self.__age)  # 对于私有静态字段,类的内部是可以访问的
        print(B.__age)
        # print(self.__money)
        print('func...')


a1 = B()
print(a1.name)
print(a1.name)

print(a1.__age)  # 实例化对象不能访问私有静态字段
print(B.__age)  # 类名也不能访问私有静态字段
                #对与私有静态字段,类的外部是不能访问的

a1.func()
#对于私有静态字段,只能在类的内部访问,类的外部,派生类均不能访问
#其实是可以访问的是Python中的一个bug(工作中千万不要用)
print(B._B__age)
print(B.__dict__)

​ B.私有方法

#私有方法
class B:
    __money = 1000  #前面加 __就是私有静态字段
    def __f1(self):  #前面加 __就是私有方法
        print('B')


class A(B):
    name = 'alex'

    def __func(self):
        print('func...')

    def func1(self):
        self.__func()  # 类的内部可以访问
        self.__f1()

a1 = A()
# a1.__func()  # 类的外部不能访问
a1.func1()  # 类的内部可以访问
a1.func1()  # 类的派生类不可以访问

七.面向对象之反射(重点)

1.反射: 用字符串数据类型的变量名来访问这个变量的值

2.反射的方法: getattr hasattr setattr delattr

  • 命名空间.xxx = getattr(命名空间, ‘xxx’)

​ 1.反射类 –> 静态属性 类方法 静态方法

s Student:
    role = 'student'
    @classmethod
    def check_course(cls):
        print('查看课程')

    @staticmethod
    def login():
        print('登录')


# 反射查看属性
print(Student.role)
print(hasattr(Student, 'role'))  # 结果是True
print(getattr(Student, 'role'))

# 反射调用方法
getattr(Student, 'check_course')()  # 类方法
getattr(Student, 'login')()  # 静态方法

num = input('>>>')
if hasattr(Student, num):
    getattr(Student, num)()

​ 2.对象的反射 –> 方法 对象属性

class A:
    def __init__(self, name):
        self.name = name

    def func(self):
        print('in func')


a = A('alex')
print(a.name)
# 反射对象属性
print(hasattr(a, 'name'))
print(getattr(a, 'name'))

# 反射对象方法
getattr(a, 'func')()

​ 3.模块的反射

mport os   # 别人写好的python代码的结合

# os.rename('__init__.py', 'init')
# getattr(os, 'rename')('nit', '__init__.py')  # == os.name
rename = os.rename
rename1 = getattr(os, 'rename')
# rename1('__init__.py', 'init')  # os.rename(__init__.py', 'init') 
rename('init', '__init__.py')  # os.rename('init', '__init__.py')

​ 1.反射自己模块中的内容

# 反射自己模块中的内容, 找到自己当前文件所在的命名空间
def wahaha():
    print('哈哈哈')

def qqing():
    print("123")


import sys
# print(sys.modules)
# import 相当于导入了一个模块
# 模块哪个导入了, 哪个没导入, 在自己的python解释器里面应该记录下来
# import sys 是一个模块, 这个模块里的所有的方法都是和python解释器相关的
# sys.modules 这个方法, 表示所有在当前这个python程序中导入的模块
# '__main__':<module '__main__' from 'D:/sylar/python_workspace/day20/4.反射.py'>
print(sys.modules['__main__'])
my_file = sys.modules['__main__']
my_file.wahaha()
my_file.qqing()

getattr(my_file, 'wahaha')()
  • 反射
    • hasattr getattr
    • 类名.名字
      • getattr(类名, ‘名字’)
    • 对象名.名字
      • getattr(对象, ‘名字’)
    • 模块名
      • getattr(模块, ‘名字’)
    • 自己文件.名字
      • import sys
      • getattr(sys.modules[‘_ main _ _’], ‘名字’)

选课系统的代码

class Manager:
    OPERATE_DIC = [
        ('创建学生账号', 'create_student'),
        ('创建课程', 'create_course'),
        ('查看学生信息', 'check_student_info')
    ]

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

    def create_student(self):
        print('创建学生账号')

    def create_course(self):
        print('创建课程')

    def check_student_info(self):
        print('查看学生信息')


class Student:
    OPERATE_DIC = [
        ('查看课程', 'check_course'),
        ('选择课程', 'choose_course'),
        ('查看已经选择的课程', 'choosed_course')
    ]

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

    def check_course(self):
        print('查看课程')

    def choose_course(self):
        print('选择课程')

    def choosed_course(self):
        print('查看已经选择的课程')


def login():
    username = input('user: ')
    password = input('pwd: ')
    with open('userinfo') as f1:
        for line in f1:
            user, pwd, ident = line.strip().split('|')
            if user == username and pwd == password:
                print('登录成功...')
                return username, ident


import sys


def main():
    usr, id = login()
    file = sys.modules['__main__']
    cls = getattr(file, id)
    obj = cls(usr)
    operate_dic = cls.OPERATE_DIC
    while True:
        for num, i in enumerate(operate_dic, 1):
            print(num, i[0])

        choice = int(input('请选择:'))
        print(len(operate_dic))
        if choice < len(operate_dic):
            choice_item = operate_dic[choice - 1]
            getattr(obj, choice_item[1])()
        else:
            print('输入有误,重新输入:')


main()

八.面向对象的进阶

  • 析构方法

    • 释放一个空间之前执行
    • 某对象借用了操作系统的资源, 还要通过析构方法归还回去: 比如–> 文件资源, 网络资源

    1.垃圾回收机制

class A:
    def __del__(self):
        # 析构方法 del A的对象, 会自动触发这个方法
        print('执行我来了')


a = A()
del a  # 对象的删除, del
print(a)

class File:
    def __init__(self, file_path):
        self.f = open(file_path)
        self.name = 'alex'

    def read(self):
        self.f.read(1024)

    def __del__(self):  # 是去归还/释放一些在创建对象的时候借用的一些资源
        # del 对象的时候 是程序员触发
        # python 解释器的垃圾回收机制, 回收这个对象所占的内存的时候, python自动触发的

        self.close()


f = File('文件名')
f.read()
# 不管是主动还是被动, 这个f对象总会被清理掉, 被清理掉就会触发__del__方法, 触发这个方法就会归还操作系统的文件资源
# python解释器在内部就能搞定的事
# 申请一块空间, 是操作系统分配给你的
# 在这一块空间中, 是由python解释器来管理的

# example :  
f = open('文件')  # python--> 操作系统 --> 硬盘里的文件 --> 文件操作符
f.close()  # f就是文件操作符
# def f
  • item系列 是和对象使用[] 访问值有联系
    • 在内置模块中,有一些特殊的方法, 要求对象必须实现_ _ getitem _ _/ _ _ setitem _ _ 才能使用
class A:
    def __getitem__(self, item):
        return getattr(self, item)

    def __setitem__(self, key, value):
        setattr(self, key, value*2)

    def __delitem__(self, key):
        delattr(self, key)


a = A()
a.k1 = 'k1'
print(a.k1)
a['k1'] = 'v1'  # 默认调用 __setitem 方法
print(a['k1'])  # 默认调用 __getitem
del a['k1']  # 默认调用__delitem
print(a['k1']) 

class B:
    def __init__(self, lst):
        self.lst = lst

    def __getitem__(self, item):
        return self.lst[item]

    def __setitem__(self, key, value):
        self.lst[key] = value

    def __delitem__(self, key):
        self.lst.pop(key)


b = B(['111', '22', 'dd', 'rr'])
print(b.lst[0])
print(b[0])
b[2] = 'alex'
print(b[2])
print(b.lst)
del b[2]
print(b.lst)
  • hash方法

    • 底层数据结构是基于hash值寻址的优化
    • hash 是一个算法
    • 能够把某一个要存在内里的值通过一系列的计算
    • ‘adf’ – > 851621 类似于这样
    • 对同一个值在多次执行python代码的时候值是不同的
    • 但是同一个值, 在同一次执行python代码的时候hash值 永远不变
    • 字典的寻址 – hash 算法 通过key的hash值去找value,所以速度比较快
    • d = {‘key’ : ‘value’}
    • hash 是一个内置函数
    • hash(obj) # obj内部必须是实现了_ _ hash _ _ 方法
    • set集合 hash不是万能的, 两个不一样的字符串, hash值可能相等–> 如果hash值相等,首先它就会判断值是否相等 == ,相同则去重, 不同则存在内存中
    • set集合的去重原理–> 通过hash算法去重的, hash的结果找到一块内存地址,只要这个地址上没有数据,就说明之前没有重复的数据, 如果这块地址上有一个数据存在了,才判断这个值和我要存的值是否一样, 如果一样,覆盖去重, 如果不一样,二次寻址给这个值换个地方存
  • eq方法

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

    def __eq__(self, other):
        if self.name == other.name and self.age == other.age:
            return True


a = A('LAEX', 10)
a1 = A('LAEX', 10)
a2 = A('LAEX', 10)
a3 = A('LAEX', 10)

print(a, a1,)
print(a == a1 == a2)  # 返回True 完全和__eq__方法一样
  • 一道经典面试题
# 一个类
# 对象的属性: 姓名 性别 年龄 部门
# 员工管理系统
# 内部转岗 python--go开发
# alex male 44 python
# alex male 55 go
# 1000个员工
# 如果几个员工对象的姓名和性别相同,这是一个人
# 请对这1000个员工做去重
class Employee:
    def __init__(self, name, sex, age, partment):
        self.name = name
        self.sex = sex
        self.age = age
        self.partment = partment

    def __hash__(self):  # 第二步 ,重写父类boject的__hash__方法, 返回hash的值,还是不行
        return hash('%s%s' % (self.name, self.sex))

    def __eq__(self, other):  # 第三步, 重写__eq__方法 ,判断值是否相等,返回True
        if self.name == other.name and self.sex == other.sex:
            return True


employ_lst = []
for i in range(100):
    employ_lst.append(Employee('alex', 'male', i, 'python'))
for i in range(100):
    employ_lst.append(Employee('wusir', 'male', i, 'go'))

employ_set = set(employ_lst)  # 第一步,先用set集合去重,结果不行
# print(employ_lst)

for person in employ_set:
    print(person.__dict__)

Author: 二哈君
Reprint policy: All articles in this blog are used except for special statements CC BY 4.0 reprint polocy. If reproduced, please indicate source 二哈君 !
 Previous
Python Python
一.面向对象1.写代码的时候,什么时候用面向对象 代码量大,功能多的时候 处理比较复杂的角色之间的关系(面试可以这么说) qq 好友 陌生人 群 组 复杂的电商程序 公司/学校的人事管理/功能的系统 面向对象的好处 代码的清晰度更高了
2020-11-07 二哈君
Next 
Python Python
一. 函数名的运用 函数名的内存地址 def func(): print("呵呵") print(func) 结果: <function func at 0x1101e4ea0> 1.函数名可以
2020-10-03 二哈君
  TOC