?类是具有一系列共同特征和行为的事件抽象概念集合。类所描述的概念和现实生活中的类概念非常相似。例如生物有很多种类,食物也有不同的种类,商品也有很多不同的种类。而一个类通过细分又可以划分各种小的分类,如动物就可以分为陆生动物、水生动物和两栖动物等等,被划分到同一个类,其都有着相似的特征和行为方式。
? 实例对象通常被定义某一个类事物的具体个体,是该类事物的一个具体表现。如人是一个类,”张三“就是人这个类的实例对象,因为他代表了一个具体类个体。
? 类变量一般是定义类中,但不在类方法中的变量,可以在整个实例化对象中公用。
? 类变量或实例变量,常用于描述实例对象的特征,如张三的姓名、身高等
? 以不严谨的方式来讲,就是定义在类中的函数称之为方法,常用于描述实例对象的行为,一般分为构造函数和常规函数。
? 就是子类可以从父类获取父类定义的属性和方法等。
? 如果父类的方法不能满足子类的要求,可以定义一个方法名与父类相同名字的方法,这个过程称之方法覆写(如Java中@Override)或方法重写
? 类是一个非常抽象的概念,而实例则代表一个具体的对象,而与对象绑定的变量可称之为实例变量。
? ? 在Python中一个简单的类定义格式如下所示:
class Person(object):
"""
定义一个人类
"""
# 类变量
classVariable="sample"
# 构造函数,也称实例化对象方法
def __init__(self,name,age):
self.name=name
self.age=age
# 类中定义的方法
def getPersonInfo(self):
return f"name is {self.name},age is {self.age}"
? 详细解释如下所示:
? ? 面向常见的三大特性:封装、继承、多态。分别如下所示:
? ? 封装主要将类的属性和方法打包为一个整体,对外部而言,仿佛是不可见的。其主要特点如下所示:
? ? 单下划线和双下划线在Python变量和方法名中都有其特殊的含义,一部分仅仅作为一种约定,一部分则含有特殊意义,详细如下所示:
? ? 前置下划线通常代表一种约定,表示仅在内部使用,通过针对编写程序员,可以简单理解为其他语言的private关键字。
? ? 通常用于解决命名冲突,在Python中,str是一个将其他类型转换为字符串的函数,那如果写str_,则可以代表重新定义一个变量或方法,在PyQT中,这种用法比较多。
? ? 在类环境中使用时会触发名称改写,对Python解释器有特殊含义
? ? 前后双置下划线的方法被称之为魔法方法,表示则Python语言本身定义的特殊方法,在自定义的属性中要避免使用这种命名方式。
? ? 通常用作临时或无意义变量名称,另外也可以表示Python REPL会话中上一个表达式的结果。
? ? 继承通常是指子类从父类中获取父类的数据和方法等等。其主要特点如下所示:
? ? 多态是指不同的对象可以执行相同的动作,但却要通过自己的实现代码来执行。国粹京剧之前都是子承父业,代代相传的艺术。假设有一对父子,父亲是非常有名的京剧艺术家,儿子长大成人,模仿父亲的戏也惟妙惟肖。有一天父亲生病,导致无法上台,而父亲的戏票已经卖出,如果退票则损失太大。因为京剧都是需要化妆后才上台,那是不是儿子可以代替父亲上台?在这里需要注意以下几点:
? ? 儿子代替父亲出演,化妆上台后就是父亲身份了。
? ? 儿子模仿再像也是模仿,只能以自己的理解来出演父亲的作品
? ? 儿子经过多年学习,其实已经有了自己独特的表演模式和行为习惯,但代替父亲出演时,是不能以自己的表演方式出演的。必须按父亲的风格去表演。
? ? 我们先来创建一个类,示例如下所示:
class Person:
def __init__(self,name,age):
self.name=name
self.age=age
def getPersonInfo(self):
return f"name is {self.name},age is {self.age}"
def run(self):
return f"{self.name} is running"
? ? 创建类的实例对象就是根据类创建具体的个体,如下所示:
person=Person("Surpass",28)
? ? 以上就完成了根据Person类实例化一个具体的人,其名字为Surpass,年龄为28,在创建实例后,我们就可以访问类的属性和方法,通过方式为:
实例对象.[属性或方法]
person=Person("Surpass",28)
print(f"name is {person.name},age is {person.age}")
person=Person("Surpass",28)
print(f"person info is {person.getPersonInfo()}")
? ? 既然类是一种事件的抽象集合,那当然也可以根据创建n个实例对象了,如下所示:
personA=Person("Surpass",28)
personB=Person("Kevin",38)
personB=Person("Lisa",20)
? ? 类中的每个属性必须有初始值,对于一些经常无需要每次赋值的属性可以指定默认值,如下所示:
class Person:
def __init__(self,name,age=28):
self.name=name
self.age=age
def getPersonInfo(self):
return f"name is {self.name},age is {self.age}"
def run(self):
return f"{self.name} is running"
# 实例化对象
personA=Person("Surpass")
? ? 看看上面的用法是不是跟函数使用很像。
? ? 如果需要修改属性的值,可以使用以下方法进行修改
1.通过实例进行修改
? ? 示例代码如下所示:
class Person:
def __init__(self,name,age=28):
self.name=name
self.age=age
def getPersonInfo(self):
return f"name is {self.name},age is {self.age}"
def run(self):
return f"{self.name} is running"
person=Person("Surpass",28)
print(f"Before modify property {person.name} {person.age}")
person.name="Lisa"
person.age=20
print(f"After modify property {person.name} {person.age}")
输出结果如下所示:
Before modify property Surpass 28
After modify property Lisa 20
2.通过调用方法进行修改
? ? 通过实例修改直接修改属性值非常方便,如下所示:
class Person:
def __init__(self,name,age=28):
self.name=name
self.age=age
def getName(self):
return self.name
def setName(self,name):
self.name=name
def getAge(self):
return self.age
def setAge(self,age):
self.age=age
def getPersonInfo(self):
return f"name is {self.name},age is {self.age}"
def run(self):
return f"{self.name} is running"
person=Person("Surpass",28)
print(f"Before modify property {person.name} {person.age}")
person.setName("Lisa")
person.setAge(20)
print(f"After modify property {person.name} {person.age}")
输出结果如下所示:
Before modify property Surpass 28
After modify property Lisa 20
? ? 熟悉Java的小伙伴同学,一眼就看出来这种跟Java很像,而更地道的Python写法如下所示:
class Person:
def __init__(self,name,age=28):
self._name=name
self._age=age
@property
def name(self):
return self._name
@name.setter
def name(self,value):
if not isinstance(value,(str,)):
raise TypeError("Expect str")
else:
self._name=value
@name.deleter
def name(self):
raise AttributeError("Can not delete attribute")
@property
def age(self):
return self._age
@age.setter
def age(self,value):
if not isinstance(value,(int,)):
raise TypeError("Expect a numer")
elif value<0:
self._age=0
else:
self._age=value
@age.deleter
def age(self):
raise AttributeError("Can not delete attribute")
def getPersonInfo(self):
return f"name is {self.name},age is {self.age}"
def run(self):
return f"{self.name} is running"
person=Person("Surpass",28)
print(f"Before modify property {person.getPersonInfo()} ")
person.name="Lisa"
person.age=-123
print(f"After modify property {person.getPersonInfo()}")
print(f"测试异常情况")
person.name=98
person.age="098"
输出结果如下所示:
Before modify property name is Surpass,age is 28
After modify property name is Lisa,age is 0
测试异常情况
Traceback (most recent call last):
File "C:/Users/Surpass/Documents/PycharmProjects/SADCI/TempCode/basicCode.py", line 52, in <module>
person.name=98
File "C:/Users/Surpass/Documents/PycharmProjects/SADCI/TempCode/basicCode.py", line 15, in name
raise TypeError("Expect str")
TypeError: Expect str
? ? 以上这种方法,必须getter、setter、deleter三个方法的名称必须一样,且必须先创建getter,后面的setter、deleter才能访问和使用,那如果已经事先定义好了getter、setter、deleter的方法,还有没有办法实现以上这种做法?答案当然是肯定的,如下所示:
class Person:
def __init__(self,name,age=28):
self._name=name
self._age=age
def getName(self):
return self._name
def setName(self,value):
if not isinstance(value,(str,)):
raise TypeError("Expect str")
else:
self._name=value
def delName(self):
raise AttributeError("Can not delete attribute")
def getAge(self):
return self._age
def setAge(self,value):
if not isinstance(value,(int,)):
raise TypeError("Expect a numer")
elif value<0:
self._age=0
else:
self._age=value
def delAge(self):
raise AttributeError("Can not delete attribute")
ageProperty=property(getAge,setAge,delAge)
nameProperty=property(getName,setName,delName)
def getPersonInfo(self):
return f"name is {self._name},age is {self._age}"
def run(self):
return f"{self._name} is running"
person=Person("Surpass",28)
print(f"Before modify property {person.getPersonInfo()} ")
person.setName("Lisa")
person.setAge(-123)
print(f"After modify property {person.getPersonInfo()}")
print(f"测试异常情况")
person.setName(98)
输出结果如下所示:
Before modify property name is Surpass,age is 28
After modify property name is Lisa,age is 0
测试异常情况
Traceback (most recent call last):
File "C:/Users/Surpass/Documents/PycharmProjects/SADCI/TempCode/basicCode.py", line 49, in <module>
person.setName(98)
File "C:/Users/Surpass/Documents/PycharmProjects/SADCI/TempCode/basicCode.py", line 13, in setName
raise TypeError("Expect str")
TypeError: Expect str
? ? 以上通过方法来修改属性的思想就是面向对象中的封装思想。但在Python,如果一个类并不存在某个属性,通过对象.属性名可以添加一个新的属性,但这种做法破坏了面向对象中的封装特性,因此不建议使用。如下所示:
class Person:
def __init__(self,name,age=28):
self._name=name
self._age=age
@property
def name(self):
return self._name
@name.setter
def name(self,value):
if not isinstance(value,(str,)):
raise TypeError("Expect str")
else:
self._name=value
@name.deleter
def name(self):
raise AttributeError("Can not delete attribute")
@property
def age(self):
return self._age
@age.setter
def age(self,value):
if not isinstance(value,(int,)):
raise TypeError("Expect a numer")
elif value<0:
self._age=0
else:
self._age=value
@age.deleter
def age(self):
raise AttributeError("Can not delete attribute")
def getPersonInfo(self):
return f"name is {self._name},age is {self._age}"
def run(self):
return f"{self._name} is running"
person=Person("Surpass",28)
# 通过实例直接增加一个新的属性,不推荐
person.country="China"
print(f"add new person property : {person.country}")
输出结果如下所示:
add new person property : China
? ? 属性常用于描述类的特征,可以通过类中定义的方法访问,也可以通过类或实例进行访问。因因此属性又可分为类属性和实例属性。
? ? 实例属性,通俗来讲是通过self.变量名称定义的属性,有时也称实例变量,属于特定的实例对象。示例如下代码如下所示:
class Person:
def __init__(self,name,age):
# 实例属性赋值
self._name=name
self._age=age
# 获取实例属性值
def getName(self):
return self._name
# 实例属性需要与实例对象进行绑定后才能访问
person=Person("Surpass",28)
print(f"{person.getName()}")
? ? Python允许声明属于类本身的变量称之为类属性或类变量,类属性无需实例化对象即可,如下所示:
class Person:
# 类属性
classVar="类属性"
def __init__(self,name,age):
# 实例属性赋值
self._name=name
self._age=age
# 获取实例属性值
def getName(self):
return self._name
# 实例属性需要与实例对象进行绑定后才能访问
person=Person("Surpass",28)
# 访问实例属性-通过实例
print(f"访问实例属性-通过实例:{person.getName()}")
# 访问类属性-通实例
print(f"访问类属性-通实例:{person.classVar}")
# 访问类属性
print(f"访问类属性:{Person.classVar}")
输出结果如下所示:
访问实例属性-通过实例:Surpass
访问类属性-通实例:类属性
访问类属性:类属性
? ? Python没有Java/C#语言的权限控制字段,如public/protected/private等等,通常的约定是以单下划线开头定义的属性为私有属性,如下所示:
class Person:
# 类私有属性
_classVar="类属性"
def __init__(self,name,age):
# 实例属性赋值
self._name=name
self._age=age
? ? Python对象中包含很多开始和结尾以双下划线的开始和结束的方法,称为特殊属性,常见的特殊属性如下所示:
特殊方法 | 含义 | 示例 |
---|---|---|
obj. __dict__ | 对象的属性字典 | int.__dict__ |
instance.__class__ | 对象所属的类 | int.__class__ |
class.__bases__ | 类的基类元组 | int.__bases__ |
class.__base__ | 类的基类 | int.__base__ |
? ? 在创建一个类,并不需要每次都新建。如果已经现成的类,其已有功能能满足你的要求,那么则可以使用继承来达到目的。一个类继承另一个类时,将自动获取另一个类的属性和方法。原有的类称为父类,新的类称为子类。子类除了继承父类的属性和方法,还可以定义自己的属性和方法,也可以重写父类的方法。
? ? 创建子类的实例时,Python首先需要完成的任务是给父类的所有属性赋值。因此子类的方法_init_()需要父类施以援手。如下所示:
1.显式调用父类构造方法
class Person:
def __init__(self,name,age):
self._name=name
self._age=age
def getPersonInfo(self):
return f"name is {self._name},age is {self._age}"
def run(self):
return f"{self._name} is running"
class Student(Person):
def __init__(self,name,age,):
super().__init__(name,age)
student=Student("Surpass",25)
print(student.getPersonInfo())
输出结果如下所示:
name is Surpass,age is 25
? ? 以上代码详解如下所示:
2.显式调用父类构造方法并添加自己的属性
? ? 示例如下所示:
class Person:
def __init__(self,name,age):
self._name=name
self._age=age
def getPersonInfo(self):
return f"name is {self._name},age is {self._age}"
def run(self):
return f"{self._name} is running"
class Student(Person):
def __init__(self,name,age,classNumber):
super().__init__(name,age)
self._classNumber=classNumber
def getStudentInfo(self):
return f"name is {self._name},age is {self._age},classNumer is {self._classNumber}"
student=Student("Surpass",25,"Class-NO.1")
print(student.getStudentInfo())
输出结果如下所示:
name is Surpass,age is 25,classNumer is Class-NO.1
3.隐式调用父类构造方法
? ? 示例如下所示:
class Person:
def __init__(self,name,age):
self._name=name
self._age=age
def getPersonInfo(self):
return f"name is {self._name},age is {self._age}"
def run(self):
return f"{self._name} is running"
class Student(Person):
pass
student=Student("Surpass",25)
print(student.getPersonInfo())
输出结果如下所示:
name is Surpass,age is 25
? ? 通过以上几种,那可以猜测一下,是不是不管子类有没有声明构造方法,都是先调用父类构造方法呢?来看看以下示例:
1.父类和子类都有定义自己构造函数
class Person:
def __init__(self,name,age):
self._name=name
self._age=age
print("1.调用父类构造方法")
def getPersonInfo(self):
return f"name is {self._name},age is {self._age}"
def run(self):
return f"{self._name} is running"
class Student(Person):
def __init__(self,name,age,classNumber):
self._name=name
self._age=age
self._classNumber=classNumber
print("2.调用子类构造方法")
def getStudentInfo(self):
return f"name is {self._name},age is {self._age},classNumer is {self._classNumber}"
student=Student("Surpass",25,"Class-No.1")
输出结果如下所示:
2.调用子类构造方法
2.父类有定义自己构造函数,子类未定义自己的构造函数
class Person:
def __init__(self,name,age):
self._name=name
self._age=age
print("1.调用父类构造方法")
def getPersonInfo(self):
return f"name is {self._name},age is {self._age}"
def run(self):
return f"{self._name} is running"
class Student(Person):
pass
输出结果如下所示:
1.调用父类构造方法
3.父类和子类都有定义自己构造函数,但子类显式调用父类构造方法
class Person:
def __init__(self,name,age):
self._name=name
self._age=age
print("1.调用父类构造方法")
def getPersonInfo(self):
return f"name is {self._name},age is {self._age}"
def run(self):
return f"{self._name} is running"
class Student(Person):
def __init__(self,name,age,classNumber):
super().__init__(name,age)
self._classNumber=classNumber
print("2.调用子类构造方法")
def getStudentInfo(self):
return f"name is {self._name},age is {self._age},classNumer is {self._classNumber}"
student=Student("Surpass",25,"Class-NO.1")
输出结果如下所示:
1.调用父类构造方法
2.调用子类构造方法
? ? 通过以上代码,可以总结出以下结论:
? ? 当一个类继承自父类后,父类的功能无法自身需求后,子类可以定义自己的属性和方法,示例如下所示:
class Person:
def __init__(self,name,age):
self._name=name
self._age=age
print("1.调用父类构造方法")
def getPersonInfo(self):
return f"name is {self._name},age is {self._age}"
def run(self):
return f"{self._name} is running"
class Student(Person):
def __init__(self,name,age,classNumber):
super().__init__(name,age)
# 子类自身定义的属性
self._classNumber=classNumber
print("2.调用子类构造方法")
# 子类自身定义的方法,显式调用父类方法
def getStudentInfo(self):
return super().getPersonInfo()
# 子类自身定义的方法
def getStudentCount(self):
studentList=[]
studentList.append(self._name)
return len(studentList)
输出结果如下所示:
1.调用父类构造方法
2.调用子类构造方法
调用父类方法的结果:name is Surpass,age is 25
调用子类自身的方法:student count is 1
? ? 在Python中覆写父类方法非常简单,只需要让子类中方法名称与父类名称一致即可,如下所示:
class Person:
def __init__(self,name,age):
self._name=name
self._age=age
print("1.调用父类构造方法")
def getPersonInfo(self):
return f"name is {self._name},age is {self._age}"
def run(self):
return f"{self._name} is running"
class Student(Person):
def __init__(self,name,age,classNumber):
super().__init__(name,age)
# 子类自身定义的属性
self._classNumber=classNumber
print("2.调用子类构造方法")
# 子类自身定义的方法
def getStudentCount(self):
studentList=[]
studentList.append(self._name)
return len(studentList)
# 覆写父类已经定义的方法
def getPersonInfo(self):
return f"name is {self._name},age is {self._age},class number is {self._classNumber}"
student=Student("Surpass",25,"Class-NO.1")
print(f"调用子类自身的方法:student count is {student.getStudentCount()}")
print(f"调用子类覆写父类的方法:{student.getPersonInfo()}")
输出结果如下所示:
1.调用父类构造方法
2.调用子类构造方法
调用子类自身的方法:student count is 1
调用子类覆写父类的方法:name is Surpass,age is 25,class number is Class-NO.1
? ? 像Java/C#等语言是单继承(即一个类同时只能继承一个父类,)和多接口继承(即一个类可继承多个接口),多层继承(因为继承的父类又继承自其他父类,看起来有多个层次),在Python中支持多重继承(即一个类可以同时继承多个类),示例如下所示:
class A:
def __init__(self):
print("1.父类构造方法-1")
def getMethonA(self):
print(f"1.父类方法{self.getMethonA.__name__}")
class B:
def __init__(self):
print("2.父类构造方法-2")
def getMethonB(self):
print(f"2.父类方法{self.getMethonB.__name__}")
class C:
def __init__(self):
print("3.父类构造方法-3")
def getMethonC(self):
print(f"3.父类方法{self.getMethonC.__name__}")
class D(A,B,C):
def getMethonD(self):
print(f"4.子类方法{self.getMethonD.__name__}")
d=D()
d.getMethonA()
d.getMethonB()
d.getMethonC()
d.getMethonD()
输出结果如下所示:
1.父类构造方法-1
1.父类方法getMethonA
2.父类方法getMethonB
3.父类方法getMethonC
4.子类方法getMethonD
? ? 使用多重继承有好也有坏
1.优点是可以调用多个父类的方法
2.缺点是如果继承的多个父类又属于同一个父类,则会出现二义性,另外也会造成逻辑上的复杂性增加,因此不推荐使用多重继承
? ? 方法是与类相关的函数,Python用于定义一个方法格式如下所示:
def 方法名称(self,参数1,参数2...):
"""函数描述"""
函数体
return 返回值
? ? 方法的定义也前面的函数是一致,唯一的区别,是参数第一个位置必须是self,在Python方法常被分为构造方法、普通方法、类方法和静态方法。
? ? 构造方法通常用于在实例化一个对象前,给对象传递一些必须的属性值,通常以__init__()做为标识符,一般格式如下所示:
class Person:
# 构造方法
def __init__(self,name,age):
self._name=name
self._age=age
? ? 除了构造方法、类方法和静态方法之外的方法称之为普通方法,这种方法一般由用户自行定义的,示例如下所示:
class Person:
# 构造方法
def __init__(self,name,age):
self._name=name
self._age=age
# 普通方法
def getName(self):
return self._name
person=Person("Surpass",28)
print(f"访问普通方法:{person.getName()}")
输出结果如下所示:
访问普通方法:Surpass
? ? 类方法与类属性非常相似,是一种属于类的方法,不需要实例化就可以访问的一种方法,但需要使用装饰器@classmethon,其语法格式如下所示,注意与普通方法的区别在于第一个参数:
@classmethod
def 方法名称(cls,参数1,参数2...):
函数体
return 返回值
? ? 示例代码如下所示:
class Person:
className="这是类属性"
# 构造方法
def __init__(self,name,age):
self._name=name
self._age=age
# 普通方法
def getName(self):
return self._name
# 类方法,可以访问类属性,但不能访问对象属性
@classmethod
def getPersonInfo(cls,hello="Hello,"):
return f"{hello} {cls.className} "
person=Person("Surpass",28)
print(f"访问普通方法:{person.getName()}")
print(f"访问类方法:{Person.getPersonInfo(‘Welcome, ‘)}")
输出结果如下所示:
访问普通方法:Surpass
访问类方法:Welcome, 这是类属性
? ? 静态方法即不需要传入self参数、也不需要传入cls参数,从面使得调用静态方法并不能获取得类中定义的属性和其他方法,但并不会影响类对象和实例对象的状态。静态方法有点像附属于类的对象的工具,与普通方法不同,调用静态方法时,只能通过类对象或实例对象,而不能脱离类对象使用,即静态方法被束缚在类对象中。其语法格式如下所示:
@staticmethod
def 方法名称(参数1,参数2...):
函数体
return 返回值
? ? 示例代码如下所示:
class Person:
className="这是类属性"
# 构造方法
def __init__(self,name,age,height,weight):
self._name=name
self._age=age
self._height=height
self._weight=weight
# 普通方法
def getName(self):
return self._name
# 类方法,可以访问类属性,但不能访问对象属性
@classmethod
def getPersonInfo(cls,hello="Hello,"):
return f"{hello} {cls.className} "
# 静态方法,不可以访问类属性
@staticmethod
def getBMI(h,w):
return w/((h/100)**2)
# 普通方法
def showBMI(self):
bmi=self.getBMI(self._height,self._weight)
return bmi
person=Person("Surpass",28,170,59)
print(f"访问普通方法:{person.getName()}")
print(f"访问类方法:{Person.getPersonInfo(‘Welcome, ‘)}")
print(f"访问静态方法:{Person.getBMI(170,59)}")
print(f"通过实例对象访问:{person.showBMI()}")
输出结果如下所示:
访问普通方法:Surpass
访问类方法:Welcome, 这是类属性
访问静态方法:20.41522491349481
通过实例对象访问:20.41522491349481
本文地址:https://www.cnblogs.com/surpassme/p/12983492.html
本文同步在微信订阅号上发布,如各位小伙伴们喜欢我的文章,也可以关注我的微信订阅号:woaitest,或扫描下面的二维码添加关注:
原文:https://www.cnblogs.com/surpassme/p/12983492.html