目录
面向对象技术简介
- 类(Class): 用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。对象是类的实例。
- 类变量:类变量在整个实例化的对象中是公用的。类变量定义在类中且在函数体之外。类变量通常不作为实例变量使用。
- 数据成员:类变量或者实例变量, 用于处理类及其实例对象的相关的数据。
- 方法重写:如果从父类继承的方法不能满足子类的需求,可以对其进行改写,这个过程叫方法的覆盖(override),也称为方法的重写。
- 局部变量:定义在方法中的变量,只作用于当前实例的类。
- 实例变量:在类的声明中,属性是用变量来表示的。这种变量就称为实例变量,是在类声明的内部但是在类的其他成员方法之外声明的。
- 继承:即一个派生类(derived class)继承基类(base class)的字段和方法。继承也允许把一个派生类的对象作为一个基类对象对待。例如,有这样一个设计:一个Dog类型的对象派生自Animal类,这是模拟"是一个(is-a)"关系(例图,Dog是一个Animal)。
- 实例化:创建一个类的实例,类的具体对象。
- 方法:类中定义的函数。
- 对象:通过类定义的数据结构实例。对象包括两个数据成员(类变量和实例变量)和方法。
类(class)
- 使用 class 语句来创建一个新类,class 之后为类的名称并以冒号结尾
class student:
'''所有同学的基类''' #doc帮助文档
info #类体
- 类的帮助信息可以通过student.__doc__查看
>>>print(student.__doc__)
所有同学的基类>>>print(st1.__doc__)
所有同学的基类
- info则由类成员、方法、数据属性组成
类变量
- 类变量即为类的属性,被所有实例化所公有。因此类变量定义在类内,但在类的方法外
class student:
"""所有同学的基类"""
# 定义一个 类变量
classnum = 11
方法
- 第一种方法init()方法是一种特殊的方法,被称为类的构造函数或初始化方法,当创建了这个类的实例时就会调用该方法
def __init__(self, name, age):
self.name = name
self.age = age
- 第二种方法add()方法是一种普通的方法,实例化后需要调用
def add(self, gender,classnum):
#不能方法间传
self.gender = gender
#可以方法间传参
student.classnum = classnum
# 普通方法
def print1(self):
print(self.name)
print(self.age)
实例化对象
s1和s2即为两个实例化对象
- 实例化时,通过init进行传参,有序或无序
- 普通方法则不必要传参
- 当然,在调用普通方法时,—init—中的参数可以传到其他方法中,即方法间传参,而普通方法的self.属性不能方法间传参,但是主函数可以调用,即为对象的属性
if __name__ == '__main__':
# 两个实例化对象st1、st2
st1 = student('yy', 21)
st2 = student(age=22,name='yiyi')
注释:由于init方法在被实例化时就要被调用,因此在定义时应当进行传参
属性
1、访问属性
我们可以使用点号 . 来访问对象的属性,例如:
>>>print(student.classnum)
11
>>>print(st1.age)
21
当然,在主函数中,我们可以修改实例的属性,包括添加,删除,修改类的属性
st1.gender = 'male' # 添加一个 'age' 属性
st11.age = 8 # 修改 'age' 属性
del st1.age # 删除 'age' 属性
当然,我们还可以使用
- getattr(obj, name[, default]) : 访问对象的属性。
- hasattr(obj,name) : 检查是否存在一个属性。
- setattr(obj,name,value) : 设置一个属性。如果属性不存在,会创建一个新属性。
- delattr(obj, name) : 删除属性。
>>>getattr(st1,'age') #访问属性
21>>>hasattr(st1,'age') #检查属性是否存在
True>>>setattr(st1,'gender','male') #设置(或修改)一个属性
>>>print(st1.gender)
male>>>delattr(st1,'gender') #删除一个属性
>>>print(st1.gender)AttributeError: 'student' object has no attribute 'gender'
2、内置类属性
- dict : 类的属性(包含一个字典,由类的数据属性组成)
- doc :类的文档字符串
- name: 类名
- module: 类定义所在的模块(类的全名是'main.className',如果类位于一个导入模块mymod中,那么className.module 等于 mymod)
- bases : 类的所有父类构成元素(包含了一个由所有父类组成的元组)
析构函数 del
析构器又称为解构器,定义的是一个实例销毁时的操作。
也就是当使用 del() 函数删除这么一个类时,它会自动调用这个类中的 __del__。
但是一般而言,解释器会自动销毁变量的,因此大多情况下,析构函数都无需重载,但是构造器则不同,它是实现实例变量的一种重要接口。
析构函数就是用于释放对象占用的资源。
例一:
class student:
"""所有同学的基类"""
# 定义一个 类变量
classnum = 11
# __init__方法,构造器
def __init__(self, name, age):
self.name = name
self.age = age
# __del__方法,析构器
def __del__(self):
print('销毁')
pass
if __name__ == '__main__':
st1 = student('yy', 21)
del st1
print(st1.age)
输出:
销毁
NameError: name 'st1' is not defined.
例二:
class student:
"""所有同学的基类"""
# 定义一个 类变量
classnum = 11
# __init__方法,构造器
def __init__(self, name):
print(name)
self.fn = open('D:\\浏览器下载\\xshell密钥.txt', 'r')
# __del__方法,析构器
def __del__(self):
print(self.fn.read())
self.fn.close()
if __name__ == '__main__':
st1 = student('yy')
del (st1)
print('over!')
输出:
yy
101210-450789-147200
over!
类的继承
通过继承创建的新类称为子类或派生类,被继承的类称为基类、父类或超类
继承语法
class 派生类(基类):
类体
多个继承
class A: # 定义类 A
类体
class B: # 定义类 B
类体
class C(A, B): # 继承类 A 和 B
类体
继承特点
-
子类继承父类构造器
如果在子类中需要父类的构造方法就需要显式地调用父类的构造方法,或者不重写父类的构造方法。子类不重写 init,实例化子类时,会自动调用父类定义的 init
class Father:
def __init__(self, name):
self.name = name
print("name:{}".format(self.name))
def getName(self):
return 'Father ' + self.name
class Son(Father):
def getName(self):
return 'Son ' + self.name
if __name__ == '__main__':
son = Son('qyh')
print(son.getName())
输出
name:qyh
Son qyh
如果重写了init 时,实例化子类,就不会调用父类已经定义的 init
我们可以使用关键字super
super().__init__(参数)
class Father:
def __init__(self, name):
self.name = name
print("name: {}".format(self.name))
def getName(self):
return 'Father ' + self.name
class Son(Father):
def __init__(self, name):
super().__init__(name)
print("hi")
self.name = name
def getName(self):
return 'Son ' + self.name
if __name__ == '__main__':
son = Son('qyh')
print(son.getName())
输出
name: qyh
hi
Son qyh
- 在调用基类的方法时,需要加上基类的类名前缀,且需要带上 self 参数变量。区别在于类中调用普通函数时并不需要带上 self 参数 ,例如
class Son(Father):
def __init__(self, name):
super().__init__(name)
Father.add(self)
self.name = name
- Python 总是首先查找对应类型的方法,如果它不能在派生类中找到对应的方法,它才开始到基类中逐个查找(类似于方法重写)
方法重写
与其说是重写,倒不如说是覆写,为什么这么说呢,且看下串代码
class Father: # 定义父类
def myMethod(self):
print('基类')
class Son(Father): # 定义子类
def myMethod(self):
print('子类')
qyh = Son() # 子类实例
qyh.myMethod() # 子类调用重写方法
输出
子类
类的私有
-
类的私有属性
类变量私有定义:__private_attrs
类内方法私有定义:self.__private_attrs
-
类的私有属性
类变量私有定义:__private_method
类内方法私有定义:self.__private_methods
类的私有属性,在类外在一定条件下不可以调用,包括输出、与输入
class JustCounter:
__secretCount = 0 # 私有变量
publicCount = 0 # 公开变量
def count(self):
self.__secretCount += 1
self.publicCount += 1
print("内部私有:{}".format(self.__secretCount))
if __name__ == '__main__':
counter = JustCounter()
JustCounter.__secretCount=1
counter.publicCount=2
counter.count()
counter.count()
print(counter.publicCount)
print("外部私有:{}".format(counter.__secretCount))
输出
内部私有:1
内部私有:2
4
外部私有:1
当然,他并不是实质上的私有,我们依然可以通过其他手段进行访问
counter._JustCounter__secretCount
我们可以通过这个方式修改私有属性
counter._JustCounter__secretCount=3