目录
一、面向对象
Python 中,一切皆对象。数据类型、函数等,都是对象。
Python采用了面向对象的思想,完全支持面向对象的基本功能:继承、多态、封装等。
将不同类型的数据、方法(即函数)放到一起,就是对象。
对象是类的具体实体,一般称为“类的实例”。类也称为“类对象”,类的实例也称为“实例对象”。
- 类(Class): 描述具有相同的属性和方法的对象的集合。定义了该集合中每个对象所共有的属性和方法。对象是类的实例。
- 类变量:类变量在整个实例化的对象中是公用的。定义在类中且在函数体之外。通常不作为实例变量使用。
- 数据成员:类变量或者实例变量, 用于处理类及其实例对象的相关的数据。
- 方法重写:从父类继承的方法不能满足子类的需求,可以对其进行改写。
- 局部变量:定义在方法中的变量,只作用于当前实例的类。
- 实例变量:也即是类里的属性变量,在类里声明,类的方法之外。
- 继承:即一个派生类(derived class)继承基类(base class)的字段和方法。继承也允许把一个派生类的对象作为一个基类对象对待。模拟"是一个(is-a)"关系
- 实例化:创建一个类的实例,类的具体对象。
- 方法:类中定义的函数。
- 对象:通过类定义的数据结构实例。对象包括两个数据成员(类变量和实例变量)和方法。
可以简单理解为:
类:动物和植物都属于类
属性:类中所有的变量
方法:类中所有函数
实例:猫狗都属于同一类(动物类),而猫狗就是动物类的实例/对象
操作规范:
- 1.类名必须符合“标识符”的规则;一般规定,首字母大写,多个单词使用“驼峰原则”。
- 2.类体中我们可以定义属性和方法。
- 3.属性用来描述数据,方法(即函数)用来描述具体执行逻辑。
class Student: #等价于class Student(object)
company = "SXT" #类属性
count = 0 #类属性
def __init__(self,name,score): #构造方法 初始化
self.name=name
self.score=score
#类里定义的方法为:实例方法(必须带有self,表示实例化)
def say_score(self): #实例方法
print('{0}的分数是:{1}'.format(self.name,self.score))
#print(f'{self.name}的分数是:{self.score}') #和上一条语句等价
s1=Student('小明',18)
s1.say_score() #小明的分数是:18
上面代码中:
- Student是类名
- __init__:构造方法,用于实例初始化
- self.name:实例属性
- company、count:类属性
- say_score():实例方法,第一个参数self代表实例化
注意!!!类里的方法为实例方法,必须带有self,表示实例化
在 Python 3 中,无论是否显示继承自 object,Python 解释器都会默认你继承 object
class Employee: #定义一个类Employee 类里包含类成员,方法,数据属性组成。
'所有员工的基类'
empCount = 0 # empCount类变量(所有成员共享)
def __init__(self, name, salary): #类的初始化(构造函数)
self.name = name #实体属性
self.salary = salary
Employee.empCount += 1
def displayCount(self): #(函数即方法)
print("Total Employee %d" % Employee.empCount)
def displayEmployee(self):
print("Name : ", self.name, ", Salary: ", self.salary)
-
empCount 变量是一个类变量,在这个类的所有实例之间共享。在内部类或外部类使用Employee.empCount (类名.类变量名)访问。
-
__init__()方法是类的构造函数(初始化方法),当创建了类的实例就会调用该方法
-
self 代表类的实例(代表当前对象的地址),self 在定义类的方法时必须有,self 不是 python 关键字。 self.__class__ 则指向类。
类的方法与普通的函数只有一个特别的区别:
它们必须有一个额外的第一个参数名称, 按照惯例它的名称是 self。
1.1 __init__构造方法和__new__方法
类是抽象的,也称之为“对象的模板”。通过类这个模板,创建类的实例对象,才能使用类定义的功能。创建对象,需要定义构造函数 init ()方法。用于执行“实例对象的初始化工作”,即对象创建后,初始化当前对象的相关属性,无返回值。
操作规范:
- 1.名称固定,必须为: init ()
- 2.第一个参数固定,必须为:self。 self 指的是刚刚创建好的实例对象。
- 3.构造函数通常用来初始化实例对象的实例属性,如下代码就是初始化实例属性:name 和 score。
def __init__ (self,name,score):
self.name = name #实例属性
self.score = score
- 4.调用构造函数:s1=Student('小明',18) ,将创建好的对象返回给变量s1
- 5.__init__()方法:初始化(给实例属性赋值)创建好的对象
- 6.new()方法: 用于创建对象,一般无需重定义该方法。
- 7.若不定义__init__方法,系统会提供一个默认的__init__方法。若定义了带参的__init__ 方法,系统不创建默认的__init__方法。
1.2实例属性和实例方法属性
实例属性 从属于 实例对象的属性,也称为“实例变量”。
实例化类其他编程语言中一般用关键字 new, Python 中并没有关键字new,类的实例化类似函数调用方式。以下使用类的名称 Student来实例化,并通过 __init__ 方法接收参数。
操作规范:
- 1.实例属性一般在 init ()方法中定义:self.实例属性名 = 初始值(self.name = name)
- 2.在实例方法中,也是通过 self 进行访问:self.实例属性名
- 3.创建实例对象后,通过实例对象访问:obj01 = 类 名() #创建对象,调用 init ()初始化属性
class Animal:
def __init__(self,name) :
self.name = name
def func(self):
print("执行此函数的逻辑")
dog = Animal(name = "大王") #创建实例对象dog,实例化时传入参数name = "大王"进入到构造方法init中,最后赋值给self.name
(1)访问属性(变量名.方法名)
实例对象. 属性
dog = Animal(name = "大王")
print(dog.name) #访问属性
(2)方法的调用
- 对象.方法名:在定义方法时self就代表了对象dog,故调用无需传参
- 类名.方法名:self参数,需要传入实例对象
dog = Animal(name = "大王")
dog.func()
Animal.func(dog)
使用以下函数的方式来访问属性:
- getattr(obj, name[, default]) : 访问对象的属性。
- hasattr(obj,name) : 检查是否存在一个属性name。
- setattr(obj,name,value) : 设置一个属性。如果属性不存在,会创建一个新属性。
- delattr(obj, name) : 删除属性。
hasattr(dog, 'name') # 如果存在 'name' 属性返回 True。
getattr(dog, 'name') # 返回 'name' 属性的值
setattr(dog, 'name', "柯基") # 添加属性 'name' 值为 8
delattr(dog, 'name') # 删除属性 'name'
1.3静态方法和类方法
-
静态方法:有
staticmethod
装饰的函数 -
类方法:有
classmethod
装饰的函数 -
实例方法:没有任何装饰器的普通函数
class Animal:
def __init__(self,name) :
self.name = name
def func(self):
print("执行此函数的逻辑")
@staticmethod
def eat():
print("执行静态方法的逻辑")
@classmethod
def jump(cls,name):
print("类方法的执行逻辑")
以上三种方法的区别:
- 普通的实例方法:定义时,他的第一个方法固定是 self,如果是从实例调用,那么 self 参数 不需要传入,如果是通过类调用,那么 self 要传入已经实例化的对象。
- 静态方法:定义时,不需要 self 参数。
- 类方法:定义时,第一个参数固定是 cls(class 简写),代表类本身。
方法和函数的区别:
都是函数
function
:类外的函数、静态方法方法(
method
):类里的方法、类方法、这些结论其实都可以使用
type
函数得到验证,代码如下
class Animal:
def __init__(self, name):
self.name = name
self.__age = 4
def run(self): #定义在类里的为方法
print(f"{self.name}跑起来啦")
@staticmethod #静态方法也是函数
def eat():
print("正在吃饭...")
@classmethod #类方法是方法
def jump(cls, name):
print(f"{name}跳起来啦")
def demo_func(): #定义在类外的为函数
pass
dog = Animal(name="金毛")
print(type(demo_func)) #<class 'function'>函数
print(type(dog.eat)) #<class 'function'>函数
print(type(dog.run)) #<class 'method'>方法
print(type(dog.jump)) #<class 'method'>方法
1.4私有变量与私有方法
下划线的使用:
-
变量名:推荐使用下划线分隔的蛇形命名法(student_name,student_age)
-
双下划线:魔法方法、构造函数需要使用
-
单下划线
_
进行占位:对于暂时用不到的变量值,可以赋值给_
进行占位
使用双下划线开头的属性变量/方法,就是一个私有变量/方法。
私有变量:以双下划线前导的变量,
实例._类名__变量名
进行访问私有方法:以双下划线前导的方法,
实例._类名__方法名()
进行访问
(1)类的私有属性:
__private_attrs:两个下划线开头,声明该属性为私有,不能在类的外部被使用或直接访问。在类内部的方法中使用时 self.__private_attrs。
(3)类的私有方法
__private_method:两个下划线开头,声明该方法为私有方法,不能在类的外部调用。在类的内部调用 self.__private_methods
class JustCounter:
__secretCount = 0 # 私有变量
publicCount = 0 # 公开变量
def count(self):
self.__secretCount += 1
self.publicCount += 1
print(self.__secretCount)
counter = JustCounter() #创建实例
counter.count()
counter.count()
print(counter.publicCount)
# print(counter.__secretCount) # 报错,实例不能访问私有变量
Python不允许实例化的类访问私有数据,但可使用 object._className__attrName( 对象名._类名__私有属性名 )访问属性:
class Runoob:
__site = "www.runoob.com"
runoob = Runoob()
print(runoob._Runoob__site) # (对象名._类名__私有属性名)访问属性:
单下划线、双下划线、头尾双下划线说明,可分为下面五种:
单前导下划线:
_var:
表示的是 protected 类型的变量,仅供内部使用。即保护类型只允许其本身与子类进行访问,不能用于 from module import *单末尾下划线:
var_:
双下划线:
__var:
表示的是私有类型(private)的变量,通过实例._类名__方法名
来调用(dog._Animal__age)只允许这个类本身进行访问根据分类,双前导和末尾下划线:
__var__:
定义的是特殊方法,一般是系统定义名字 ,类似 __init__() 之类的。单下划线:
_
涉及到访问控制的有:
_var
和__var
Python并没有真正的私有化支持,但可用下划线得到伪私有。可以尽量避免定义以下划线开头的变量。
1.5python内置类属性
- __dict__ : 类的属性(包含一个字典,由类的数据属性组成)
- __doc__ :类的文档字符串
- __name__: 类名
- __module__: 类定义所在的模块(类的全名是'__main__.className',如果类位于一个导入模块mymod中,那么className.__module__ 等于 mymod)
- __bases__ : 类的所有父类构成元素(包含了一个由所有父类组成的元组)
class Employee:
'所有员工的基类'
empCount = 0
def __init__(self, name, salary):
self.name = name
self.salary = salary
Employee.empCount += 1
def displayCount(self):
print("Total Employee %d" % Employee.empCount)
def displayEmployee(self):
print("Name : ", self.name, ", Salary: ", self.salary)
print("Employee.__doc__:", Employee.__doc__)
print("Employee.__name__:", Employee.__name__)
print("Employee.__module__:", Employee.__module__)
print("Employee.__bases__:", Employee.__bases__)
print("Employee.__dict__:", Employee.__dict__)
输出结果如下:
1.6python垃圾回收
Python 使用了引用计数这一简单技术来跟踪和回收垃圾。在 Python 内部记录着所有使用中的对象各有多少引用。内部跟踪变量,称为引用计数器。当对象被创建时 就创建了一个引用计数, 当这个对象不再需要时(对象的引用计数变为0 ), 它被垃圾回收。但是回收不是"立即"的, 由解释器在适当的时机,将垃圾对象占用的内存空间回收。
垃圾回收机制不仅针对引用计数为0的对象,同样也可以处理循环引用(两个对象相互引用,但是没有其他变量引用他们)。Python 的垃圾收集器实际上是一个引用计数器和一个循环垃圾收集器。作为引用计数的补充, 垃圾收集器也会留心被分配的总量很大(即未通过引用计数销毁的那些)的对象。 在这种情况下, 解释器会暂停下来, 试图清理所有未引用的循环。
二、类的使用
2.1类的封装Encapsulation
封装:将数据与具体操作的实现代码放在某个对象内部,使代码实现细节不被外界发现,外界只能通过接口使用该对象,而不能修改对象内部实现。封装,离不开“私有化”(将类或者函数中的某些属性限制在某个区域之内,外部无法直接调用)
封装就是:对于私有变量、私有方法的调用不建议直接用上述方法调用,而是使用统一的接口来对私有变量进行查看,调用。封装能隐藏对象实现细节,使代码更易维护,且保证了系统安全性。类通过将函数和变量封装在内部,实现了比函数更高一级的封装。
class Person:
def __init__(self, name, age):
self.name = name
self.__age = age #私有变量__age
def is_adult(self):
return self.__age >= 18
xh = Person(name="小红", age=17) #调用者就不会知道is_adult方法内部的实现细节,只会知道最后是判断
xm = Person(name="小明", age=20)
print(xh.is_adult()) #False
print(xm.is_adult()) #True
2.2类的继承Inheritance
面向对象编程的主要好处之一:通过继承机制实现代码的重用。继承最大的好处:子类获得了父类的全部变量和方法的同时,又可以根据需要进行修改、拓展。被继承的类称为基类(父类),继承而得的类叫派生类(子类)
class 子类(父类):
python中继承中的一些特点:
- 1、如果在子类中需要父类的构造方法就需要显式的调用父类的构造方法,或者不重写父类的构造方法。
- 2、在调用父类的方法时,需要加上父类的类名前缀,且需要带上 self 参数变量。区别在于类中调用普通函数时并不需要带上 self 参数
- 3、Python 总是首先查找对应类型的方法,如果它不能在子类中找到对应的方法,它才开始到父类中逐个查找。
(1)单继承
# 父类定义
class People:
def __init__(self, name, age, weight):
self.name = name
self.age = age
def speak(self):
print(f"{self.name} 说: 我{self.age}岁。")
# 单继承示例
class Student(People):
def __init__(self, name, age, weight, grade):
# 调用父类的实例化方法
People.__init__(self, name, age, weight)
self.grade = grade
xm = Student(name="小明", age=10, weight=50, grade="三年级")
xm.speak()
由于继承的机制,Student 实例会拥有 People 类所有属性和方法,比如下边我可以直接调用 People 类的 speak 方法。
若不想继承父类的speak方法,可以重写覆盖父类的speak()方法:
# 父类定义
class People:
def __init__(self, name, age, weight):
self.name = name
self.age = age
def speak(self):
print(f"{self.name} 说: 我{self.age}岁。")
# 单继承示例
class Student(People):
def __init__(self, name, age, weight, grade):
# 调用父类的实例化方法
People.__init__(self, name, age, weight)
self.grade = grade
def speak(self): #重写speak方法
print(f"{self.name}说:我{self.age}岁了,在读{self.grade}了")
print("{}说:我{}岁了,在读{}了".format(self.name,self.age,self.grade))
xm = Student(name="小明", age=10, weight=50, grade="三年级")
xm.speak()
(2)多继承
继承参数的书写有先后顺序,写在前面的被优先继承。
class 子类(父类1, 父类2, 父类3...):
class D:pass
class C(D):pass
class B(C):
def show(self):
print("i am B")
class G:pass
class F(G):pass
class E(F):
def show(self):
print("i am E")
class A(B, E):pass
调用:
a = A()
a.show()
多继承的顺序:使用的是从左向右再深度优先的原则
如果在继承元组中列了一个以上的类,那么它就被称作"多重继承"
class Parent: # 定义父类
parentAttr = 100
def __init__(self):
print("调用父类构造函数")
def parentMethod(self):
print( '调用父类方法')
def setAttr(self, attr):
Parent.parentAttr = attr
def getAttr(self):
print( "父类属性 :", Parent.parentAttr)
class Child(Parent): # 定义子类
def __init__(self):
print ("调用子类构造方法")
def childMethod(self):
print ('调用子类方法')
c = Child() # 实例化子类
c.childMethod() # 调用子类的方法
c.parentMethod() # 调用父类方法
c.setAttr(200) # 再次调用父类的方法 - 设置属性值
使用issubclass()或者isinstance()方法来检测。
- issubclass(sub,sup) :判断一个类是另一个类的子类或者子孙类
- isinstance(obj, Class):如果obj是Class类的实例对象或者是一个Class子类的实例对象则返回true。
2.3类的多态Polymorphism
多态:是指在同一类型下的不同形态。
class People:
def speak(self):
pass
class American(People):
def speak(self): #speak为重写,因为父类有speak方法,子类没使用进行了重写
print("Hello, boys")
class Chinese(People):
def speak(self):
print("你好,老铁")
p1 = American()
p2 = Chinese()
American 和 Chinese 都继承了 People 类,但他们在 speak()
函数下,却有不同的形态表现。American 说英文,Chinese 说汉语。
2.4类的 property 属性
为了实现属性的合法性校验,Python 引入的 property (内置装饰器)属性。
对象的属性:通过把变量值赋值给对象本身来实现的。
class Student:
@property
def age(self):
return self._age
@age.setter
def age(self,value):
if 0 <=value <=120:
self._age = value
else:
raise ValueError("valid value must be in [0,120]")
student = Student()
student.age = -5 #报错:ValueError: valid value must be in [0,120]
property
,是 Python 中一个内置的装饰器,它可以在新式类中把一个函数 改造
成属性。
-
当你读取属性值时,会进入被
property
装饰的函数。 -
当你对属性进行赋值时,会进入被
@xx.setter
装饰的函数。 -
两个装饰器,一定是
@property
在前面,而@xx.setter
在后