Python面向对象知识点详解(含__init__、__add__、__str__、issubclass()、isinstance()等方法的使用)

类(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. 形式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
  1. 形式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)

猜你喜欢

转载自blog.csdn.net/weixin_42782150/article/details/109738477