note
一切皆对象
OOP的核心思想
类: 从近似的对象中抽象出类
对象: 然后再从类出实例化出对象
OPP三大特性
封装
封装就是将抽象的数据(变量)和行为(函数)打包,形成一个逻辑上的整体(即类);
封装可以增强安全性(数据)并简化编程(函数),用户只能通过类对外接口的访问权限来使用类的成员。
继承
一个类可以以 class newclsname():来开始全新构造(实际上会默认继承自object);
也可以从某个已经存在的类继承。继承的类叫做subclass。比如要构造蜘蛛这个类,可以从昆虫这个类继承。构建学生,雇员,战士都可以从人这个类继承。
多态
因为类具有继承关系,子类可以向上转型被看做是父类的类型,比如无论是战士还是快递员,都是人类。也因为有了继承关系,子类可以继承父类的所有方法和属性,当然也可以重载父类的成员函数及属性。例如,当子类(直升机)和父类(飞机)都存在相同的fly()方法时,子类的fly()覆盖了父类的fly(),在运行时就总是会调用子类的fly()。这就是继承带来的多态。
关于类与对象操作的BIFs
type() 返回对象类型
id(), 查看对象id
dir(), 查看对象下变量及函数
issubclass(),isinstance(), super(),类,实例,调父类
hasattr(),getattr(),setattr(),delattr()类属性操作
globals(),locals(), 全局与局部的名称空间
import(),reload(), 模块的导入与重载
class B:
a = 2
pass
class A(B): #A 继承 B
a = 1
__d = 2 #__d被隐藏
def __init__(self):
self.b = 2
self.__c = 3
def func1(self):
print(super().a) # 访问父类的属性
'''
定义静态方法,跟普通函数没什么区别,与类和实例都没有所谓的绑定关系,它只不过是碰巧存
在类中的一个函数而已。不论是通过类还是实例都可以引用该方法。
静态方法的使用场景:
如果在方法中不需要(也不能)访问任何实例方法和属性,纯粹地通过传入参数并返回数据的功能性方法,
那么它就适合用静态方法来定义,它节省了实例化对象的开销成本,往往这种方法放在类外面
的模块层作为一个函数存在也是没问题的,而放在类中,仅为这个类服务。
'''
@staticmethod
def func2():
pass
@classmethod # 类方法
def func3(cls):
pass
class C:
pass
a = A()
type(a) #__main__.A
id(a) #地址
dir(a) # [...'a', 'b', 'func1', 'func2', 'func3']
dir(A) # [...'a', 'func1', 'func2', 'func3']
issubclass(A, B) # true
isinstance(a,C) # False
a.func1() # 2
hasattr(A, 'func3') # True
getattr(A, 'a') # True # getattr(A, '__d')报错
setattr(A, 'a', 100) # 设置类属性a的值为100, 会影响所有其类的对象的属性a的值
setattr(a, 'a', 200) # 设置对象a的属性a的值为200, 不会影响类属性a的值
a.a # 200
A.a # 100
delattr(a, 'a') #删除对象a的属性a,不影响类的属性a
a.a #100
delattr(A, 'a') #删除类A的属性a, 但是还有个从父类继承过来的属性a
a.a # 2
delattr(B, 'a') #把类B的属性a也删除
a.a #报错 'A' object has no attribute 'a'
globals()
locals()
类的创建和实例化
创建类
class关键字
指定继承
定义类的成员:属性(类变量,实例变量),方法(类方法,实例方法)
实例化类
#完成一个学生类的设计
#要求能查看总学生人数,全部学生姓名,毕业分数标准1000,已经毕业学生数量。
#实现考试方法,成绩分数如果大于60分代表其通过考试,分数计入该学生总分。
#如果该学生累计分数达到1000分,该学生即毕业
#实现查分方法,向该学生返回是否已经毕业,或者还差多少分数才能毕业
#实现查询所有已毕业学生数量的功能
#实现查询所有学生数量的功能
#实现查询所有学生姓名的功能
class Student():
student_total = 0
student_list = []
student_graduated = 0
pass_score = 1000
def __init__(self, name, age, gender):
self.name = name
self.age = age
self.gender = gender
self.__score = 0
Student.student_total += 1
Student.student_list.append(name)
def exam(self, score):
if score < 60 :
return 'Sorry, failed!'
else:
self.__score += score
return 'Good, your score is updated to '+str(self.__score)+' now!'
if self.__score >= Student.pass_score :
Student.student_graduated += 1
def check(self):
if self.score < Student.pass_score:
return Student.pass_score - elf.score, ' scores left to achieve!'
else:
return 'You have graduated!'
@classmethod
def get_student_list(cls):
return Student.student_list
@classmethod
def get_student_graduate(cls):
return Student.student_graduated
类的内部结构
数据成员: [用于处理类及实例对象的相关数据]
类变量: 在类中且在函数体外,实例之间共享
实例变量: 定义在方法中, 作用于当前实例的类
方法成员(在类中定义的函数叫方法) :
类方法:定义时需要使用@classmethod装饰器, 第一个参数为cls
实例方法:绑定到实例的方法, 第一个参数为self,
静态方法[普通方法]:1)定义的时候使用@staticmethod装饰器。2)静态方法没有参数限制, 不需要实例参数self和类参数cls 3)静态法可以通过类名访问, 也可以通过实例访问。
# 实例方法:
# 是指该类的每个实例都可以调用到的方法。只有实例能调用实例方法。定义时第一个形参为self
# 类方法:
# 类方法是将类本身作为对象进行操作的方法。类对象和实例都可以调用类方法。
#定义时以@classmethod进行装饰,其第一个参数是类,约定写为cls。
# 实例方法和类方法都是依赖于python的修饰器来实现。
#对象方法以self参数,类方法以cls参数来传递。
# cls
# cls是指向类的指针,在类方法中第一个形参要命名为cls.
# self
# self是指向每个独立对象的指针.在实例方法中第一个形参被命名为self,以区别其它函数。
既然@staticmethod和@classmethod都可以直接类名.方法名()来调用,那他们有什么区别呢
从它们的使用上来看,
@staticmethod不需要表示自身对象的self和自身类的cls参数,就跟使用函数一样。
@classmethod也不需要self参数,但第一个参数需要是表示自身类的cls参数。
如果在@staticmethod中要调用到这个类的一些属性方法,只能直接类名.属性名或类名.方法名。而@classmethod因为持有cls参数,可以来调用类的属性,类的方法,实例化对象等,避免硬编码。
类的继承与多态
继承
创建一个类时可以从新开始, 也可以从已经有的类继承下来
super()子调用父类的方法
多态
因为类具有继承关系, 子类可以向上转型被看做是父类的类型, 比如
无论是战士还是快递员, 都是人类。
有了继承关系, 子类可以继承父类的所有方法和属性, 当然也可以重
载父类的成员函数及属性。
例如, 当子类(直升机) 和父类(飞机) 都存在相同的fly()方法时, 子
类的fly()覆盖了父类的fly(), 在运行时就总是会调用子类的fly()。
这就是继承带来的多态。
# 多态的概念是应用于Java和C#这一类强类型语言中,
# Python是弱类型语言,Python崇尚“鸭子类型”
# 在Python世界中,一切都是对象
class A:
def bar(self):
print ('A.bar')
class B(A):#继承自A
def car(self):
print ('B.car')
#和A,B类型不相关的类C
class C():
def sar(self):
print ('C.sar')
def car(self):
print('C.car')
def fun1(obj):#接收一个参数obj
obj.car()#调用该参数下的car()方法。而不关心obj到底是什么类型~
b1=B()
c1=C()
fun1(b1)#一切皆对象,不care是什么class
fun1(c1) #一切皆对象,不care是什么class
访问控制
Python没有像其它语言有访问控制的关键字, 例如private、 protected等等。 Python通过命名约定来实
现的访问控制
对模块级的控制, 通过在标识符前加单下划线 _ 实现。
对类内部的属性及方法, 通过在在标识符前加双下划线 __ 来实现的私有化
类中两个双下划线包裹的属性或方法为特殊方法 __func__
Magic Method
魔法方法就是可以给你的类增加魔力的特殊方法, 如果你的对象实现(重载) 了这些方法中的某一个, 那么这个方法就会在特殊的情况下被 Python 所调用,你可以定义自己想要的行为, 这些会自动发生。它们经常是两个下划线包围来命名的。(后续补)
python 中 __name__ == ‘__main__’ 的作用
有句话经典的概括了这段代码的意义:
“Make a script both importable and executable”
意思就是说让你写的脚本模块既可以导入到别的模块中用,另外该模块自己也可执行。
如果我们是直接执行某个.py文件的时候,该文件中那么” __name__ == ‘__main__’ “是True,但是我们如果从另外一个.py文件通过import导入该文件的时候,这时__name__的值就是我们这个py文件的名字而不是__main__。
这个功能还有一个用处:调试代码的时候,在”if name == ‘main’“中加入一些我们的调试代码,我们可以让外部模块调用的时候不执行我们的调试代码,但是如果我们想排查问题的时候,直接执行该模块文件,调试代码能够正常运行!
模块与包
模块module定义:一个.py文件,包含了对象定义与语句
模块作用:用来从逻辑上组织代码
模块使用:
搜索路径(标准模块, 自定义与第三方模块)路径
搜索路径设置(修改sys.path(sys.path.append()), 设置环境变量)
导入方法
import test#作为模块空间导入
from module_name import class_name #指定模块下具体的类, 对象导入,并入当前空间
from module_name import * #将模块下所有对象导入, 并入当前空间
包package定义:
含有__init__.py的文件夹被称为包, __init__.py文件用于标识当前文件夹是一个包.(该文件可以为空,但必须有),包用于组织模块, 通常把功能相近的模块进行再次封装成为包。
包的目录结构:模块,子包,子包下的子包
包的安装, 导入与访问
包的安装(pip,conda)
不同的导入方式(假设包名为test):
import test#导入__init__.py这个moduel。
from test import * #导入__init__.py这个moduel下所有对象导入到当前空间。
from test.test_level1.test_level2 import test_level2 #导入的层次目录下的模块。还是模块
import sys
sys.path
sys.path.append('d:/')