Python面向对象
类(Class)、对象的基本概念:
类(Class): 用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。对象是类的实例。
对象:通过类定义的数据结构实例。对象包括两个数据成员(类变量和实例变量)和方法。
备注:
\quad
数据成员:类变量或者实例变量, 用于处理类及其实例对象的相关的数据。
\quad
对象的两个数据成员:
- 类变量:类变量在整个实例化的对象中是 公用的 。类变量定义在类中且在函数体之外。类变量通常不作为实例变量使用。
- 实例变量:在类的声明中,属性 是用变量来表示的。这种变量就称为实例变量,是在类声明的内部但是在类的其他成员方法之外声明的。
方法:类中定义的函数。在类的内部,使用 def 关键字可以为类定义一个方法,与一般函数定义不同,类方法必须包含参数 self,且为第一个参数。形如:def count(self, param1, param2, ...):
备注:
类的方法 与 普通函数 只有一个特别的区别——类的方法,必须有一个额外的第一个参数名称, 按照惯例它的名称是self
。
局部变量:定义在方法中的变量,只作用于当前实例的类。
实例化:创建一个类的实例,类的具体对象。
继承:即一个派生类(derived class)继承基类(base class)的字段和方法。继承也允许把一个派生类的对象作为一个基类对象对待。例如,有这样一个设计:一个Dog类型的对象派生自Animal类,这是模拟"是一个(is-a)"关系(例图,Dog是一个Animal)。
方法重写:如果从父类继承的方法不能满足子类的需求,可以对其进行改写,这个过程叫方法的覆盖(override),也称为方法的重写。
示例代码:
范例一:
"""以下代码的功能:创建了一个新的类实例并将该对象赋给局部变量 x,x 为空的对象。"""
class MyClass:
"""一个简单的类实例"""
i = 123 # 定义一个类的属性(变量)
def __init__(self): # 类定义了 __init__() 方法,类的实例化操作会自动调用 __init__() 方法
self.data = []
def f(self): # 定义一个类的方法:使用 def 关键字可以为类定义一个方法,与一般函数定义不同,类方法必须包含参数 self,且为第一个参数
return "hello world"
# 实例化类,实例化类 MyClass,对应的 __init__() 方法就会被自动调用:
x = MyClass() # 如果不进行实例化,直接访问类的属性和方法时会报错,NameError: name 'x' is not defined
# 访问类的属性和方法
print("MyClass 类的属性 i 为:", x.i)
print("MyClass 类的方法 f 为:", x.f())
>>>
MyClass 类的属性 i 为: 123
MyClass 类的方法 f 为: hello world
范例二:
# 类定义
class people:
# 定义基本属性
name = ''
age = 0
# 定义私有属性,私有属性在类外部无法直接进行访问
__weight = 0
# 定义构造方法
def __init__(self, n, a, w):
self.name = n
self.age = a
self.__weight = w
def speak(self):
print("%s 说:我 %d 岁。" % (self.name, self.age))
# 实例化类
p = people("runoob", 10, 30)
p.speak()
>>>
runoob 说:我 10 岁。
创建类
使用 class 语句
来创建一个新类, class 之后为类的名称并以冒号结尾。
示例代码:
class Employee:
"""所有员工的基类"""
empCount = 0 # empCount 变量是一个类变量,它的值将在这个类的所有实例之间共享。你可以在内部类或外部类使用 Employee.empCount 访问。
def __init__(self, name, salary): # 第一种方法__init__()方法是一种特殊的方法,被称为类的构造函数或初始化方法,当创建了这个类的实例时就会调用该方法
self.name = name # self 代表类的实例,self 在定义类的方法时是必须有的,虽然在调用时不必传入相应的参数。
self.salary = salary
Employee.empCount += 1
def displayCount(self):
print("Total Emplyee %d" % Employee.empCount)
def dispalyEmployee(self):
print("Name:", self.name, "Salary:", self.salary)
# (1)创建实例对象
# 创建 Employee 类的第一个对象
emp1 = Employee("Zara", 2000)
# 创建 Employee 类的第二个对象
emp2 = Employee("Manni", 5000)
# (2)访问属性
emp1.dispalyEmployee()
emp2.dispalyEmployee()
print("Total Employee %d" % Employee.empCount)
>>>
Name: Zara Salary: 2000
Name: Manni Salary: 5000
Total Employee 2
# (3)添加,删除,修改类的属性,如下所示:
emp1.age = 7 # 添加一个 'age' 属性
emp1.age = 8 # 修改 'age' 属性
print("emp1.age", emp1.age)
>>>
emp1.age 7
del emp1.age # 删除 'age' 属性
print("emp1.age", emp1.age)
>>>
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-11-9a8accbf3d69> in <module>
----> 1 print("emp1.age", emp1.age)
AttributeError: 'Employee' object has no attribute 'age'
-
empCount 变量是一个类变量,它的值将在这个类的所有实例之间共享。你可以在内部类或外部类使用 Employee.empCount 访问。
-
第一种方法__init__()方法是一种特殊的方法,被称为类的构造函数或初始化方法,当创建了这个类的实例时就会调用该方法;
-
self 代表类的实例,而非类。self 在定义类的方法时是必须有的,虽然在调用时不必传入相应的参数。
类属性与方法
类的私有属性
__private_attrs:两个下划线开头,声明该属性为私有,不能在类的外部被使用或直接访问。在类内部的方法中使用时 self.__private_attrs
。
类的方法
在类的内部,使用 def 关键字可以为类定义一个方法,与一般函数定义不同,类方法必须包含参数 self,且为第一个参数
类的私有方法
__private_method:两个下划线开头,声明该方法为私有方法,不能在类的外部调用。在类的内部调用时 self.__private_methods
。
关于Python中的私有属性和基本属性的区别:
\quad
通常我们约定:两个下划线开头的属性是私有的(private)。其他为公共的(public) 。
1、类内部可以访问私有属性(方法);
2、类外部不能直接访问私有属性(方法);
3、Python不允许实例化的类访问私有数据,但你可以使用object._className__attrName( 对象名._类名__私有属性名 )
访问属性。
\quad
单下划线、双下划线、头尾双下划线说明:
\quad
__foo__
: 定义的是特殊方法,一般是系统定义名字 ,类似 init() 之类的;_foo
: 以单下划线开头的表示的是 protected 类型的变量,即保护类型只能允许其本身与子类进行访问,不能用于 from module import *__foo
: 双下划线的表示的是私有类型(private)的变量, 只能是允许这个类本身进行访问了。
示例代码——定义私有属性/方法:
#############定义私有属性/方法
class Demo:
__price = 25.8 # 私有类属性
def __init__(self, u_name, u_age):
self.uname = u_name # 共有属性(变量)
self.__uage = u_age # 私有属性(变量)
def __age(self): # def __age(self)定义私有方法
print("这是私有方法")
print("调用共有属性:", self.uname)
print("调用私有属性:", self.__uage)
print("调用私有类属性:", self.__price)
def name(self): # def name(self)可以为类定义一个方法,与一般函数定义不同,类方法必须包含参数 self,且为第一个参数
print("这是公有方法")
print("调用共有属性:", self.uname)
print("调用私有属性:", self.__uage)
print("调用私有类属性:", self.__price)
###############定义私有属性和私有方法,同时正确实现类内、类外调用私有属性和私有方法
# 完整代码实现如下:
# 类的私有方法的定义和调用
class JustCounter:
# 定义类的属性
__secretCount = 0 # 私有变量——两个下划线开头,表示私有变量
publicCount = 0 # 公开变量
# 定义类的方法(通过def 关键字 实现)
# 在类的内部,使用 def 关键字可以为类定义一个方法,与一般函数定义不同,类方法必须包含参数 self,且为第一个参数
def count(self):
self.__secretCount += 1 # 私有变量,在类内部的方法中调用时,通过self.__private_attrs的形式实现
self.publicCount += 1 # 公开变量,在类内部的方法中调用时
print("self.__secretCount:", self.__secretCount)
# 类的外部调用
counter = JustCounter() # 实例化
counter.count() # 第一次调用
>>>
self.__secretCount: 1
counter.count() # 第二次调用
>>>
self.__secretCount: 2
counter.count() # 第三次调用
>>>
self.__secretCount: 3
print("counter.publicCount:", counter.publicCount) # 类的外部可以直接调用共有属性
>>>
counter.publicCount: 3
print("counter.__secretCount", counter.__secretCount) # 类的外部直接调用类的私有属性会报错
# 报错,实例不能访问私有变量
>>>
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-21-8237e2171602> in <module>
----> 1 print("counter.__secretCount", counter.__secretCount) # 类的外部直接调用类的私有属性会报错
AttributeError: 'JustCounter' object has no attribute '__secretCount'
## 解决方案:
# Python不允许实例化的类访问私有数据,但你可以使用 object._className__attrName( 对象名._类名__私有属性名 )访问属性
print("counter.__secretCount", counter._JustCounter__secretCount)
>>>
counter.__secretCount 3
参考链接:
关于Python中的私有属性和私有方法
Python 面向对象
__init__()
类的构造函数(初始化方法)
__init__()
方法是一种特殊的方法,被称为 类的构造函数 或 初始化方法。类定义了 __init__() 方法
,类的实例化操作会自动调用 __init__() 方法
。
如下实例化类 MyClass,对应的 __init__() 方法
就会被调用:
x = MyClass()
初始化方法的2种常见形式:
- 形式1:
def_init_(self)
, 进行实例化时,需要 先实例化,再进行赋值 。
这种形式 在__init__方法中,只有一个self
,它代表类的实例,而非类 。
示例代码:
"""在类内定义的方法内部,包含两个属性,name, grade。它允许定义一个空的结构,当新数据来时,可以直接添加。"""
## 方法一:def __init__(self)初始化类
class Student_Grade:
def __init__(self):
self.name = None # 属性1
self.grade = None # 属性2
def print_grade(self):
print("%s grade is %s" % (self.name, self.grade))
# 先实例化,再进行赋值
s1 = Student_Grade() # 创建对象s1
s1.name = 'Tom'
s1.grade = 8
# 先实例化,再进行赋值
s2 = Student_Grade() # 创建对象s2
s2.name = 'Jerry'
s2.grade = 7
s1.print_grade()
s2.print_grade()
>>>
Tom grade is 8
Jerry grade is 7
- 形式2:
def_init_(self, 参数1,参数2,···,参数n)
,进行实例化时,直接传入参数即可 。
示例代码:
"""这种形式在__init__方法中时,就直接给定了两个参数name和grade,且属性值不允许为空。
"""
### 方法二:def __init__(self, name, grade)初始化类
class Student_Grade02:
def __init__(self, name, grade): # #类似于C++中的有参构造函数
self.name = name
self.grade = grade
def print_grade(self):
print("%s grade is %s" % (self.name, self.grade))
# 进行实例化时,直接传入参数即可
s1 = Student_Grade02("Tommy", 9) # 创建对象s1
s2 = Student_Grade02("Honey", 12) # 创建对象s2
s1.print_grade()
s2.print_grade()
>>>
Tommy grade is 9
Honey grade is 12
备注:
\quad
1、self是形式参数,当执行s1 = Student(“Tom”, 8)时,self等于s1;当执行s2 = Student(“sunny”, 7)时,self=s2。
2、两种初始化方法def_init_(self)
与def_init_(self, 参数1,参数2,···,参数n)
的区别在于:
(1)定义函数时属性赋值是否允许为空;
(2)进行实例化时是否直接传入参数。
显然,第二种更为简洁。
类的继承
面向对象的编程带来的主要好处之一是 代码的重用 ,实现这种重用的方法之一 是通过继承机制 。
通过继承创建的新类称为子类或派生类,被继承的类称为基类、父类或超类。
继承语法:
(1)继承单个类:
class 派生类名(基类名):
...
(2)继承多个类
class A: # 定义类 A
.....
class B: # 定义类 B
.....
class C(A, B): # 继承类 A 和 B
.....
你可以使用issubclass()
或者isinstance()
方法来检测。
issubclass()
-——布尔函数,判断一个类是另一个类的子类或者子孙类,语法:issubclass(sub,sup)
isinstance(obj, Class)
——布尔函数,如果obj是Class类的实例对象或者是一个Class子类的实例对象,则返回True。
# 多继承的典型格式
class DerivedClassName(Base1, Base2, Base3):
<statement-1>
.
.
.
<statement-N>
需要注意圆括号中父类的顺序,若是父类中有相同的方法名,而在子类使用时未指定,python从左至右搜索,即:方法在子类中未找到时,从左到右查找父类中是否包含方法。
示例代码:
# 继承
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()
print("\n实例化子类", c) # 实例化子类
print("\n调用子类的方法", c.childMethod()) # 调用子类的方法
print("\n调用父类方法", c.parentMethod) # 调用父类方法
print("\n再次调用父类的方法——设置属性值", c.setAttr(200)) # 再次调用父类的方法——设置属性值
print("\n再次调用父类的方法——获取属性值", c.getAttr())
>>>
调用子类构造方法
实例化子类 <__main__.Child object at 0x107106e10>
调用子类方法
调用子类的方法 None
调用父类方法 <bound method Parent.parentMethod of <__main__.Child object at 0x107106e10>>
再次调用父类的方法——设置属性值 None
父类属性: 200
再次调用父类的方法——获取属性值 None
示例代码——多继承:
# 定义类
class people:
# 定义基本属性
name = ''
age = 0
# 定义私有属性,私有属性在类外部无法直接进行访问
__weight = 0
# 定义构造方法
def __init__(self, n, a, w):
self.name = n
self.age = a
self.weight = w
def speak(self):
print("%s 说:我 %d 岁。" % (self.name, self.age))
# 单继承示例
class student(people):
grade = ''
def __init__(self, n, a, w, g):
# 调用父类的构造函数
people.__init__(self, n, a, w)
self.grade = g
# 覆写父类的方法
def speak(self):
print("%s 说:我 %d 岁了,我在读 %d 年级" %(self.name, self.age, self.grade))
# 另一个类,多重继承之前的准备
class speaker():
topic = ''
name = ''
def __init__(self, n, t):
self.name = n
self.topic = t
def speak(self):
print("我叫 %s, 我是一个演说家,我演讲的主题是 %s" % (self.name, self.topic))
# 多重继承
class sample(speaker, student):
a = ''
def __init__(self, n, a, w, g, t):
student.__init__(self, n, a, w, g)
speaker.__init__(self, n, t)
# 调用类
test = sample("Tim", 25, 80, 4, "Python") # 实例化一个对象,并赋初值
test.speak() # 方法名同,默认调用的是括号中排前的父类的方法
方法重写
如果你的父类方法的功能不能满足你的需求,你可以在子类重写你父类的方法:
示例代码:
# 方法重写
class Parent: # 定义父类
def myMethod(self):
print("调用父类方法")
class Child(Parent): # 定义子类
def myMethod(self): # 在子类中进行方法的重写
print("调用子类方法")
c = Child() # 子类实现
c.myMethod() # 子类调用重写方法
基础重载方法
序号 | 方法 | 描述 & 简单的调用 |
---|---|---|
1 | __init__ ( self [,args...] ) |
构造函数,在生成对象时调用,简单的调用方法: obj = className(args) |
2 | __del__( self ) |
析构方法, 释放对象时使用,删除一个对象,简单的调用方法 : del obj |
3 | __repr__( self ) |
打印,转换。转化为供解释器读取的形式,简单的调用方法 : repr(obj) |
4 | __str__( self ) |
用于将值转化为适于人阅读的形式,简单的调用方法 : str(obj) |
5 | __cmp__ ( self, x ) |
对象比较,简单的调用方法 : cmp(obj, x) |
6 | __setitem__ |
按照索引赋值 |
7 | __getitem__ |
按照索引获取值 |
8 | __len__ |
获得长度 |
9 | __cmp__ |
比较运算 |
10 | __call__ |
函数调用 |
11 | __add__ |
加运算 |
12 | __sub__ |
减运算 |
13 | __mul__ |
乘运算 |
14 | __truediv__ |
除运算 |
15 | __mod__ |
求余运算 |
16 | __pow__ |
乘方 |
__str__( self )
的使用
python 在打印一个实例化对象时,打印的是对象的地址,比如:<main.Vector01 at 0x111cd9f10>,然而调用__str__(self)方法
就可以打印出具体的属性,用法如下:
# 运算符重载
class Vector01:
def __init__(self, a, b): # 初始化方法,调用类时会直接自动调用该方法
self.a = a
self.b = b
print("self.a:", self.a)
print("self.b:", self.b)
def __str__(self): #
print("运行__str__函数") # 使用print函数打印时,会调用父类的该方法
return "Vector (%d, %d)" % (self.a, self.b)
# 创建实例对象v1
v1 = Vector01(2, 10)
# 直接打印v1,输出对象地址
v1
>>>
<__main__.Vector01 at 0x11218b550>
# 定义了def __str__(self),使用print函数打印时,会直接调用父类的该方法
print("v1:", v1)
>>>
v1: 运行__str__函数
Vector (2, 10)
运算符重载
__add__(self, other)
的使用
具体说来,每个变量在Python中都是以对象形式存在的,即都是继承于Object。而Object则具有object.__add__(self, other)
这样一个方法。每当处理 x + y x+y x+y 这一个表达式的时候,将会调用x.__add__(y)
。另外为了“交换律”,还有__radd__方法
,也就是用来处理 y + x y+x y+x ,这样会调用y.__radd__(x)
。
备注:
__add__()
函数接收的两个参数都是对象,所以 要系统识别是两个对象相加才能进行调用这个函数, 如果return 也是对象,这样就可以 当 print 的时候,调用类内自定义的__str__方法
。
示例代码:
# 运算符重载
class Vector01:
def __init__(self, a, b):
self.a = a
self.b = b
print("self.a:", self.a)
print("self.b:", self.b)
def __str__(self):
print("运行__str__函数")
return "Vector (%d, %d)" % (self.a, self.b)
def __add__(self, other): # 接收2个参数的类型都是对象
print("两个对象相加:")
return Vector01(self.a + other.a, self.b + other.b) # 返回对象
v1 = Vector01(2, 10)
>>>
self.a: 2
self.b: 10
v2 = Vector01(5, -2)
>>>
self.a: 5
self.b: -2
print("v1:", v1)
>>>
v1: 运行__str__函数
Vector (2, 10)
print("v2:", v2)
>>>
v2: 运行__str__函数
Vector (5, -2)
v1+v2
>>>
两个对象相加:
self.a: 7
self.b: 8
<__main__.Vector01 at 0x111cd9f10>
print("v1+v2", v1+v2)
>>>
v1+v2 运行__str__函数
Vector (7, 8)
参考链接:
重写add方法(python)
内置函数—特定算术的运算__add__、radd、__iadd__等
示例代码——算术运算符重载:
class Num:
def __init__(self,math):
self.math = math
def __str__(self):
return str(self.math)
## 对象出现在 + 号的左边
def __add__(self, other):
return self.math +other
## 对象出现在 + 号的右边
def __radd__(self, other):
return self.math +other
#+=运算时自动触发,若没有用add
def __iadd__(self, other):
return Num(self.math + other)
n = Num(12)
# ret = n +10
# ret = 10 + n
n += 50
print(n)
##### 特定算术运算符
加法:__add__ __radd__ __iadd__
减法:__sub__ __rsub__ __isub__
乘法:__mul__ __rmul__ __imul__
除法:__truediv__ __rtruediv__ __itruediv__
求余:__mod__ __rmod__ __imod__
#### 关系运算
class Num:
def __init__(self,a):
self.a = a
##大于
def __gt__(self, other):
return self.a > other
#小于
def __lt__(self, other):
return self.a < other
#等于 , == 会触发,不实现__ne__,!=也会触发该方法
def __eq__(self, other):
return self.a == other
#大于等于
def __ge__(self, other):
return self.a >= other
#小于等于
def __le__(self, other):
return self.a <= other
#不等于: !=
def __ne__(self, other):
return self.a != other
n = Num(20)
print(n > 10)
print(n < 10)
print(n == 10)