06-Python类与对象

什么是类

百度百科:

类是对象的抽象,对象是对客观事物的抽象。

用通俗的话来说:

类是类别的意思,是数据类型。
对象是类别下的具体事物。

也就是说:

类是数据类型,对象是变量。

比如:

自定义一种数据类型:水果。那水果就是类。
由此数据类型创建一个变量:一个苹果。那这个苹果就是对象。
类是抽象的,对象是具体的。

类和对象的关系

类是一种数据类型,相当于一个模板,用于创建对象。
每一个对象都有自己独立的内存空间,用于存储数据成员。

比如:

人都有名字,年龄,性别等信息。这是一个类,是抽象的。
小明叫张明,18岁,男。这是一个对象,是具体的。

专业术语

封装了变量和函数的一种自定义的数据类型

  • 数据成员

类中的变量

  • 方法

类中的函数

  • 对象

由类创建的变量称作为对象

  • 实例化

创建一个对象。
因为类是抽象的,而对象是具体的,是一个实例,所以通过类创建对象的过程称为实例化。

  • 继承

根据父类创建子类,子类 继承 父类中所有变量和方法

数据成员

示例:

class Student:      #定义类
    pass
s=Student()         #创建对象(类的实例化)
s.i=12345           #创建或修改类的数据成员
print(s.i)          #访问类的数据成员

结果:

12345

解释:

使用关键字class定义一个类
类名为Student,通常类名首字母大写。
对象名为s
i 为数据成员
访问类中成员的语法:对象名.成员名

每个对象都有单独的内存空间去存储自己的普通数据成员(后面会讲到静态数据成员,就不一样了)

方法

类中的函数称为方法
方法可以访问此类所有的数据成员和方法。

示例:

class Student:
    def f(self):    #定义方法
        print("hello world")
s=Student()         #创建对象(类的实例化)
s.f()               #调用类的方法

结果:

hello world

解释:

f 为方法名
访问类中成员的语法:对象名.成员名
类的方法与普通的函数区别:第一个参数为self。
self实际上是对象的地址。所以通过self访问的成员都属于当前对象。

带有参数的方法:

class Student:
    def f(self,s):    #定义方法
        	print(s)
s=Student()
s.f("hello world")    #调用类的方法 (输出:hello world)

解释:

字符串hello world将传给s

self

示例:

class Student:
    def f(self):
        print(self.i)   #访问类中的属性
s=Student()   
s.i=12345
s.f()

结果:

12345

解释:

类的方法与普通的函数区别:第一个参数为self。
self实际上是对象的地址。
每一个对象的self地址都不一样,它们都指向本身。
通过self可以访问自身的数据成员和方法。

访问数据成员和方法必须使用self,否则就是临时变量,而不是数据成员

示例:

class Student:
    def f(self):
        i=200       #临时变量
        	print(self.i)   #数据成员
        	print(i)
s=Student()
s.i=100           #创建类的普通数据成员
s.f()

结果:

100
200

解释:

通过结果可以看出,两个i是不同的变量
通过self访问的是对象内部的内存空间,是当前对象的成员。

构造方法

构造方法:创建对象时自动调用此方法

class Student:
    def __init__(self):
        print("对象被创建")
s=Student()

结果:

对象被创建


数据成员

普通数据成员

普通数据成员存储在对象自己的内存空间中。
所以每个对象的普通数据成员都是独立的,因为它们的内存空间不一样。

示例:

class Student:
    def f(self):
        print(self.i)       #在类中访问普通数据成员
s=Student()
s.i=100           #创建类的普通数据成员
s.i=200           #修改类的普通数据成员
print(s.i)        #访问类的普通数据成员
s.f()

解释:

当数据成员不存在时, s.i=100将创建此数据成员

当数据成员存在时,s.i=100将修改此数据成员

self是对象的地址,用于访问对象自己的属性,包括数据成员和方法

一般在构造函数中创建普通数据成员

在构造函数中创建普通数据成员:

class Student:
    def __init(self):
        self.a=1        #定义普通数据成员
        self.b=2        #定义普通数据成员

解释:

当创建对象时会自动调用构造方法,所以数据成员也被自动创建。
若不使用构造方法,需要在手动的、依次给每一个对象创建这些数据成员。(很麻烦)

静态数据成员

静态数据成员存储在类中,是所有对象共享的。
通过类名访问静态成员,而不是对象名。

示例:

class Student:
    name="张三"
print(Student.name)     #访问静态数据成员
Student.name="李四"     #修改静态数据成员

解释:

静态数据成员在类中直接定义
通过类名访问静态数据成员,因为是共享的
通过对象名访问普通数据成员,因为每个对象之间是独立的

注意:也可以通过对象名访问静态数据成员,但不建议这样

class Student:
    name="张三"
s1=Student()
s2=Student()

print("----创建之前:----")
print("Student.name :",Student.name)
print("s1.name :",s1.name)

print("----创建之后:----")
s1.name="李四"
print("Student.name :",Student.name)
print("s1.name :",s1.name)

结果:

----创建之前:----
Student.name : 张三
s1.name : 张三
----创建之后:----
Student.name : 张三
s1.name : 李四

解释:

可以看到,当没有创建普通数据成员时:s1.name表现为静态数据成员

当创建普通数据成员后:
s1.name表现为普通数据成员

得出结论:不建议用对象名去修改静态数据成员

私有数据成员

私有数据成员只能被当前类所访问,外部和子类中都无法访问。
这样做的是为了数据和代码安全。

示例:

class Student:
    __name="张三"    #创建类的静态私有数据成员
    def __init__(self):
        self.__age=18   #创建私有数据成员
s=Student()

解释:

若在类的外部访问类的私有成员,则会报错:AttributeError。
私有数据成员名:使用双下划线开头。
静态私有数据成员:私有的,所有对象共享的通过类名访问

外部和子类中都无法访问私有成员,那如何访问它们呢?借助类中非私有方法作为桥梁

class Student:
    def __init__(self):
        self.__name="张三"
    def getName(self):          #通过此方法访问静态成员__name
        return self.__name
    def setName(self,name):     #通过此方法修改静态成员__name
        self.__name=name
s=Student()
s.setName("李四")
print(s.getName())      #输出:李四

解释:

无法直接访问私有数据成员
类的内部可以访问私有数据成员。如非私有方法。
得出结论:通过非私有方法间接访问私有数据成员。

特殊的数据成员

这些特殊的数据成员已经存在,无需自己创建
如__dict__将会返回类中所有的数据成员,以字典返回

class Student:
    def __init__(self):
        self.a=1
        self.b=2
s=Student()
print(s.__dict__)       #输出{'a': 1, 'b': 2}


方法

普通方法

class Student:
    def show(self,s):
        print("我是"+s)
s=Student()
s.show("学生")       #输出:我是学生

私有方法

在类的外部和子类中无法访问私有方法
私有方法仅供内部使用
私有方法名用双下划线开头

class Student:
    def __show(self):  #外部无法直接访问
        	print("Private")
s=Student()
s.__show()      #外部无法访问私有方法,报错!!!!

静态方法

静态方法是共享的,属于类,而不是某个对象
静态方法只能访问静态数据成员

class Student:
    @classmethod
    def show(cls,s):
        print(cls,s)  #输出类名
Student.show("张三")       #输出:<class '__main__.Student'> 张三

解释:

方法前的@classmethod表示:此方法为静态方法

与普通方法类似,有一个额外的参数cls,cls是class的缩写,代表类的地址。

因为静态方法是共享的,所以通过类名调用静态方法。

构造方法

创建对象时Python自动调用构造方法

通常在构造方法中做对象的初始化工作,如创建数据成员。

构造方法名固定为__init__。

class Student:
    def __init__(self):
        self.name=""
        self.age=0
s=Student()

析构方法

回收对象时Python自动调用析构方法
通常在析构方法中做对象的收尾工作,如关闭打开的文件。

class Student:
    def __del__(self):
        print("对象已被删除")
s=Student()
对象已被删除

解释:

析构方法名固定为__del__。

程序运行完毕时会回收所有变量和内存空间,所以会删除对象s。

除此之外,代码块运行完毕也会回收临时变量,如函数中的临时变量。

__call__方法

__call__方法:对象名可以当作方法名使用,调用__call__方法的函数体和形参

class Student:
    def __call__(self):
        print("调用__call__")
s=Student()
s()     #输出:调用__call__

解释:

可以看到__call__方法的调用方式不是s.__call__(),而是通过对象名调用s()

强制转换

通过定义强制转换的方法,可以将对象转换成任意数据类型。

class Student:
    def __int__(self):
        return 18
s=Student()
a=int(s)        #a的值为18

解释:

若要转换为int,需要定义__int__方法
强制转换的语法为int(obj)

若要转换为其他类型,如float,需要定义__float__方法。

应用-得到学生的成绩
class Student:
    def __init__(self,name,age):
        self.name=name
        self.age=age
    def __int__(self):
        return self.age
s=Student("张三",20)
a=int(s)        #得到学生s的年龄
应用-得到学生的信息
class Student:
    def __init__(self,name,age):
        self.name=name
        self.age=age
    def __str__(self):
        return "我是一名学生,姓名:%s,年龄:%d"%(self.name,self.age)
s=Student("张三",20)
print(s)    #直接输出,就会调用str(obj)

结果:

我是一名学生,姓名:张三,年龄:20

解释:

类似的还有float等数据类型,也都可以做强制转换
若要转换为其他类型,如float,需要定义__float__方法。

运算符重载

对象之间可以进行加减乘除等操作,如:

s1=Student()
s2=Student()
s3=s1-s2

那s3的值到底是多少?到底是s1和s2身高差?还是平均分差?还是其他属性的差值?
这需要我们自己来定义,进行运算符的重载。

示例-重载符号+:

class Student:
    def __init__(self,name,age):
        self.name=name
        self.age=age
    def __add__(self,other):        #运算符重载
        	return self.age + other.age
s1=Student("张三",20)
s2=Student("李四",18)
print(s1+s2)    #输出38 (20+18=38)

解释:

重载符号+的方法名固定为:add
参数固定为self和other,分别表示左操作数和右操作数。
通过return得到结果。

常用的符号与方法名

符号 方法名 描述
+ __add__
- __sub__
* __mul__
/ __truediv__
// __floordiv__ 整除
% __mod__ 取模
** __pow__
下标运算符重载

通过下标运算符,可通过obj[n]这种形式访问对象中的数据

示例-通过下标查询:

class Student:
    def __init__(self,name,age):
        self.name=name
        self.age=age
    def __getitem__(self, item):  # 使用obj[n]时调用
        	if type(item) == slice:     #切片
          	     print(item.start)  #开始下标
         	     print(item.stop)   #结束下标
          	     print(item.step)   #步长
     	return item
s1=Student("张三",20)
print(s1[1:8:7])

结果:

1
8
7
slice(1, 8, 7)

示例-通过下标修改:

class Student:
    def __init__(self):
        self.score={"语文":98,"数学":96}
    def __setitem__(self, key, value):
        self.score[key]=value
s1=Student()
s1["语文"]=100
print(s1.score)

结果:

{'语文': 100, '数学': 96}

示例-通过下标删除:

class Student:
    def __init__(self):
        self.score={"语文":98,"数学":96}
    def __delitem__(self, key):
        del self.score[key]
s1=Student()
del s1["语文"]
print(s1.score)

结果:

{'数学': 96}


属性

通过定义属性,可以把函数变成数据成员

示例:

class Student:
    @property
    def name(self):           #调用方式print(obj.name)
        return "张三"
    @name.setter
    def name(self, value):    #调用方式obj.name="Fei"
        print(value)
    @name.deleter
    def name(self):           #调用方式del obj.name
        print("del name")
s=Student()
print(s.name)       #输出:张三
s.name="李四"       #输出:李四
del s.name          #输出:del name

解释:

使用@property将函数变成数据成员
name.setter:name()的set函数
name.deleter:name()的del函数
属性就像数据成员一样使用,实际上是函数



继承

当我们想为某个类添加新的方法时,无需重新定义所有方法,使用继承即可。
例如:普通学生可以得到成绩信息,艺术生还能得到艺术成绩。

好处:

代码重用,方便维护。
通过继承,可以得到所有父类的方法,不必再复制一份代码。

基类&派生类

已存在的类称为基类(父类),新建的类称为派生类(子类)。

注意点:

子类不能直接访问父类的私有成员

示例:

class A:
    def show(self):
        print("我是函数show()")
class B(A):           #继承了A
    pass
b=B()
b.show()        #因为B继承了A,所以b拥有函数show()

解释:

C继承了A和B,所以拥有A和B的所有方法
子类不能直接调用父类的私有成员

多重继承

子类可以再被继承。
如:A被B继承,B又被C继承。

class A:
    pass
class B(A):             #继承了A
    pass
class C(B):             #继承了B
    pass
c=C()

函数重写

继承父类的函数之后,可以设置同名函数,这是新函数会把旧函数覆盖。

class A:
    def show(self):
        print("我是A")
class B(A):   
    def show(self):         #重写了show函数
        print("我是B")
b=B()
b.show()    #输出:我是B

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章