A 面向对象:
你写代码时,什么情况下要用面向对象
1 处理比较复杂的角色之间关系的时候: qq/电商程序 公司/学校的人事管理
2 可读性 代码清晰度更高,无论开发者还是调用者都能明确地分辨出每个角色拥有的属性和方法
3 增强了代码的可扩展性
4 增加复用性
5 更加规范
B python当中一切皆对象 基础数据类型都是对象
C 类型和自定义类的关系 类型和类是一个东西
type(obj) 如果obj是一个对象,那么他的type就是他的类型
D 创建一个对象
类名( ) 实例化
__new__( ) 创造了一个对象的空间,一些简单的初始化
E 创建一个类 类是由元类创建的
class 类名 语法级别 python解释器读到这句话的时候,自动触发某件事创建一个类
type是所有类的元类, 就如object是所有类的父类一样
类也是被创建出来的,type创建类,type(class) = type
所有类的元类和类型都是type
抽象类 class A(metaclass=ABCMeta) ABCMeta创建了这个A类,那么ABCMeta就是A的元类,如果不指定的话默认是type
抽象类不能被实例化,其子类必须实现被abstractmethod装饰的方法,否则就会报错.
type(obj) 的结果就是这个obj所属的类
type(类名) 的结果就是创建这个类的元类,大多数情况下就是type,除非你指定metaclass
class Leiming
类是什么时候被加载的,以及类名是什么时候生效的
class Person:
ROLE = 'CHINA' #类的代码不是定义函数代码,即使没有实例化也会加载,所以会进行print
print(Person.ROLE) #会报错,因为类的代码全部加载完之后Person才指向内存空间,类名才会生效,也才能使用类名+ 点的指向方式
def func(self):
pass
类的静态属性,动态属性都存在类里面
对象通过指针找到类空间中的内容(比如类中的方法)
对象内部本身也存储了一些只属于对象的属性
对象: 构造new ,初始化init
组合: 一个类的对象作为另一个对象的属性, 组合表示什么有什么的关系
继承: 继承表示什么是什么的关系 主要目的是节省代码
单继承和多继承,多继承是特色
单继承:如果子类的对象调用某个方法,子类有就调用子类的,子类没有就调用父类的
子类有但想调用父类的:
1 super: 不用自己传self super(子类,self).方法名(除了self之外的参数)
2 父类名.方法名(self)
注意在任何类中调用的方法,都要自行分辨一下这个self到底是谁的对象
子类和父类
#多继承
#新式类 广度优先 C3算法
#mro()方法查看继承顺序
#py3 默认继承object,所以py3都是新式类
# super().func() 遵循mro算法,在类的内部不用传子类名和self
#py2 需要主动继承object
#super(子类名,self).func()必须传类名和self
#经典类 深度优先
#py2 不继承object,默认都是经典类
#没有mro()方法
class A:
def func(self):print('A')
class B(A):
def func(self):
super().func()
print('B')
class C(A):
def func(self):
super().func()
print('C')
class D(B,C):
def func(self):
super().func()
print('D')
d = D()
d.func() #mro()继承顺序为dbca,则打印结果为ACBD
b = B() #B只有但继承所以继承顺序为ba,打印结果为AB
抽象类和接口类
#抽象类和接口类(只在java中有区别,在python中实际上没有区别)
# 都不能被实例化
# 规范子类当中必须实现某个方法
#有原生的实现抽象类的方法,但是没有原生实现接口类的方法
#如果要遵循java的开发规范:
#抽象类: 抽象类中的方法可以实现,只能单继承
#接口类: 可以多继承,但是这个类中的方法你都不应该实现
#java
# java 只支持类的单继承 抽象类 父类的方法可以实现
# 接口 interface 支持多继承的规范 接口类中的所有方法只能写pass
#抽象类
#
#接口类
多态 鸭子类型
#多态
#一种的类型的多种形态 多个子类去继承父类,每一个子类都是这个父类的一种形态,即为多态
class Animal:pass
class Tinger(Animal):pass
class Frog(Animal):pass
#处处是多态 因为所有类都是object的子类
#java
def func(Frog obj):
obj.eat()
#python
def func(obj):
obj.eat()
#鸭子类型 规范全凭自觉
封装 私有的
#封装 私有的
#广义的封装:把方法和属性都封装在一个类里,定义一类规范来描述一类事物
#狭义的封装: 私有化.只能在类的内部访问
#私有举例: __静态变量,私有方法,私有的对象属性,私有的类方法,私有的静态方法
#在内存中的存储方式: _类名__名字
#为什么在类的内部可以使用双下划线访问:在类的内部使用,你就知道你在哪个类中,可以自行加上对应的类前缀
#在子类中可以访问父类的私有变量吗?不行.
#私有:不能在类的外部使用也不能继承(即使是子类),因为不能自动获得正确的前缀
property
#property 装饰器函数 内置函数,帮助你将类中的方法伪装成属性,(有人命名他为 特性 )
#调用方法的时候不需要主动加括号
#让程序的逻辑性更合理
#@方法名.setter 装饰器,修改被 property装饰的属性的时候,会调用被这个装饰器装饰的方法,除了self之外还有一个参数,被修改的值
#使用setter的场景,当需要对属性修改操作添加条件判断时,可以将此属性设置为静态属性,通过函数添加判断条件,条件为真则修改静态属性,
# 并用property将函数伪装为属性,用setter修改静态属性操作通过此函数触发
#@方法名.deleter 装饰器,当要删除被property装饰的属性的时候,会调用被这个装饰器装饰的方法 实际上不会真的删除
#只用property的场景: 方法计算结果本身就是一个属性,但是这个属性会随着这个类/对象的一些基础变量的变化而变化
#偏其他语言的使用场景: property+私有的 合用,这个时候更多的也会用到setter和deleter
classmethod 类方法
#classmethod 既是类方法的装饰器,也是一个内置函数
#使用类名调用,默认传类名作为第一个参数
#使用场景:不用对象命名空间中的内容,而用到了类命名空间中的变量(静态变量),类方法或者静态方法
class Goods:
__discount = 0.8
def __init__(self,price):
self.__price = price
@property
def price(self):
return self.__price * Goods.__discount
@classmethod
def change_discount(cls,num):
cls.__discount = num
apple = Goods(10)
banana = Goods(15)
print(apple.price,banana.price)
apple.change_discount(1)
print(apple.price,banana.price)
staticmethod 既是静态方法的装饰器,也是一个内置函数
#staticmethod 既是静态方法的装饰器,也是一个内置函数
#使用场景: 如果一个类里面的方法,既不需要用到self中的资源(属性/变量),也不用cls中的资源,相当于一个普通的函数
#但是出于某种原因,还要把这个方法放在类中,就将这个方法变成一个静态方法
#原因列举:
# 想用完全的面向对象模式编程,所有函数都必须写到类里
# 某个某个功能确实是这个类的方法,但是没有用到和这个类有关系的资源,比如login()
#举例如下
class Person:
@staticmethod
def login(): #动作,属于某一个对象
pass
class Student(Person): pass
class Manager(Person):pass
class Course: pass
class Classes: pass
# 另一个用到staticmethod的场景 object.__new__() 源码
insert(n) ,pop(n) 在列表中增删,执行效率低,不建议使用
append(),pop()增删末尾,效率高
反射
# 反射
# 从某个指定命名空间中用字符串数据类型的变量名来获取变量的值
# 反射应用4种场景
# 1类名反射 静态属性,类方法,静态方法
# 2对象名反射 对象属性 方法
# 3模块 模块中的方法
# 4自己模块中
import sys
mymodule = sys.moules['__main__']
getattr(mymodule,'变量名')
hasattr()/getattr()/setattr()/delattr()
#参数
#(命名空间,'变量名')
#setattr(命名空间,'变量名',新的值)
#使用场景: 只能拿到字符串版本的变量名
#拿到字符串变量名的场景 1从文件里拿 2从交互获得 input/网络传输
a = '你好'
print(a.encode('utf-8'))
内置方法
#进阶
#内置方法/魔术方法/双下方法
#__名字__ 内置方法都不是被直接调用的,而是间接调用的
#内置函数/面向对象中特殊语法/python提供的语法糖(python内置的可触发双下方法的简单符号,例如 + - * /)
#__str__: str(obj)执行前提是,要求必须实现了__str__,要求这个方法的返回值必须是字符串str类型
# print %s str
#__call__ :对象() 必须用他的场景:用类写装饰器
# __len__: len(obj)执行的前提是,obj必须实现了__len__方法,要求这个函数的返回值必须是数字int类型
#__new__ : 在实例化的过程中,最先执行的方法(在执行__init__之前),用来创造一个对象的方法(构造方法)
#应用举例: 通过判断内存空间是否存在 实现单例类
#__init__: 在实例化的过程总,在执行__new__之后,自动触发的一个初始化方法
__repr__() 方法
#__str__: str(obj),要求必须实现了__str__,要求这个方法的返回值必须是字符串str函数 #print %s str #__repr__: 是__str__ 的备胎 如果有__str__方法,那么 print %s str 都先去执行__str__方法,并且使用__str__的返回值 # 如果没有__str__方法,那么 print %s str 都会执行repr, __rerp的返回值也是字符串 # 执行双下函数优先级说明:当需要执行__str__函数但子类中没有的时候,如果父类是object,则优先执行子类的__repr__, # 如果父类不是object,则优先执行父类的__str__,可简单总结为 继承优先替代 # 而 repr(ojb) ,%r 无论什么情况都会直接执行__repr__ #__str__ __repr__ :如果两者只能实现一个,建议优先实现__repr__ (因为关联指令更多) # a = '123' # print(a) # print(repr(a)) # # class A: # def __init__(self,name): # self.name = name # def __str__(self): # return '**%s**'%self.name # def __repr__(self): # return self.name # a = A('alex') # print(a) # print(str(a),repr(a)) # print('%s | %r'%(a,a))