Python面向对象---类的基本使用

时间:2023-02-12 08:59:11

 ✅作者简介:热爱科研的算法开发者,Python、Matlab项目可交流、沟通、学习。

 ????个人主页:算法工程师的学习日志

Python面向对象---类的基本使用

1、面向对象

类(class):是一种用来描述具有相同属性和方法的对象的集合。

类变量:类变量在整个实例化的对象中是公用的。一般定义在类中且在函数体之外。

方法:类中的函数

数据成员:类变量或者实例变量用于处理类及其实例对象的相关的数据。

方法重写:如果从父类继承的方法不能满足子类的需求,可以对其进行改写,这个过程叫方法的覆盖(override),也称为方法的重写。

局部变量:定义在方法中的变量,只作用于当前实例的类。

实例变量:在类的声明中,属性是用变量来表示的,这种变量就称为实例变量,实例变量就是一个用 self 修饰的变量。

继承:即一个派生类(derived class)继承基类(base class)的字段和方法。继承也允许把一个派生类的对象作为一个基类对象对待。就像我们定义一个fruit(水果)类,然后又定义了一个fruit类的派生类apple(苹果),它有着fruit类的一些属性和方法,也有着自己的一些独特的属性和方法,和fruit类是一种’is-a’的关系。

实例化:类的一个具体对象,类像当于一个模板,只有我们将其实例化为一个对象后才能对其进行相应的操作。

对象:通过类定义的数据结构实例。对象包括两个数据成员(类变量和实例变量)和方法。


2、类定义

定义一个类:

class ClassName:
....
....
....

类名建议采用驼峰式命名,或者全部大写字母


3、使用类对象方法

类对象支持两种操作:属性引用和实例化

属性引用:和python中的其他语法一样,obj.name

在类中带__的属性为类的私有属性,私有属性在类外部无法直接访问,像__name.

class Fruit:
#这是类的一个基本属性
self.number = 100
def get_number(self):
a = self.number + 100
return a




f = Fruit()
print('We have {0} fruits'.format(f.number))
print('We have {0} fruits'.format(f.get_number()))

输出结果:

We have 100 fruits
We have 200 fruits

4、构造方法

python类中有一个名为__init__()的特殊方法,叫构造方法,该方法在类进行实例化时会自动进行调用(可以用于类属性初始化等),类似C++里面类的构造函数。


def __init__(self):
self.data = []

类定义了 __init__() 方法,类的实例化操作会自动调用__init__()方法。

class Fruit:
def __init__(self):
print('你已经实例化了一个对象')


f = Fruit()

输出结果

你已经实例化了一个对象

init_() 方法可以有参数,参数通过 init() 传递到类的实例化操作上。

class Complex:
def __init__(self,real,image):
self.r = real
self.i = image
def get_complex(self):
print('complex real is %.2f , image is %.2f'%(self.r,self.i))


a = Complex(3.5,-3)
a.get_complex()


输出结果如下:

complex real is 3.50 , image is -3.00

self代表类的实例,而非类。类的方法与普通的函数只有一个特别的区别——它们必须有一个额外的第一个参数名称, 按照惯例它的名称是 self。但self并不是Python中的关键字哦。


不知是否可以这样理解,self就代表的是你按照一个类实例化一个对象后的对象的地址。很像C++类中this指针

class Test:
def prt(self):
print(self)
print(self.__class__)


t = Test()
t.prt()

输出:

<__main__.Test object at 0x0000025EC6D45608>
<class '__main__.Test'>

5、类的方法

在类的内部,使用 def 关键字来定义一个方法,与一般函数定义不同,类方法必须包含参数 self, 且为第一个参数。如果不需要self传递参数,需要在函数前面加上@staticmethod,表示静态方法

class Complex:
def __init__(self, real=None, image=None):
self.r = real
self.i = image


def get_complex(self):
print('complex real is %.2f , image is %.2f' % (self.r, self.i))


@staticmethod
def test(a, b):
print('complex real is %.2f , image is %.2f' % (a, b))




a = Complex(3.5, -3)
a.get_complex()


b = Complex()
b.test(3, -2)

输出结果

complex real is 3.50 , image is -3.00
complex real is 3.00 , image is -3.00

6、继承

Python 同样支持类的继承,格式如下:

class Derivedclassname(Baseclassname):
...
...

Baseclassname(基类名)必须与派生类定义在一个作用域内。除了类,还可以用表达式,基类定义在另一个模块中时这一点非常有用:

class Fruit:
def __init__(self,sweet):
self.sweetness = sweet
def describe(self):
print('Our fruit has a sweetness of %.2f'%self.sweetness)


class Apple(Fruit):#单继承,继承fruit类
def __init__(self,sweet,color):
self.color = color
Fruit.__init__(self,sweet)
def describe(self):#改写基类fruit的方法
print('Our apple has a sweetness of {0:.2f}%,and color is {1}'.format(self.sweetness,self.color))




apple = Apple(62.2,'red')
apple.describe()

输出:

Our apple has a sweetness of 62.20%,and color is red


多继承

Python同样可以继承多个基类:

class Derivedclassname(basename1,basename2,...):
...
...
...

需要注意圆括号中父类的顺序,若是父类中有相同的方法名,而在子类使用时未指定,python从左至右搜索,即方法在子类中未找到时,从左到右查找父类中是否包含方法。

class Fruit:
def __init__(self, sweet):
self.sweetness = sweet

def describe(self):
print('Our fruit has a sweetness of %.2f' % self.sweetness)




class Food:
def __init__(self, uprice, num):
self.unit_price = uprice
self.number = num
self.total_price = num * uprice

def cost(self):
print('You need to pay {0:.3} yuan, thank you'.format(self.total_price))




class Apple(Fruit, Food):
def __init__(self, sweet, color, uprice, num):
self.color = color
Fruit.__init__(self, sweet)
Food.__init__(self, uprice, num)

def describe(self):
print('Our fruit has a sweetness of {0:.2f}%,and color is {1}'.format(self.sweetness, self.color))


apple = Apple(62.2,'red',3.5,21)
apple.describe()
apple.cost()

输出:


Our fruit has a sweetness of 62.20%,and color is red
You need to pay 73.5 yuan, thank you

7、方法重写

如果父类方法的功能不能满足你的需求,你可以在子类重写你父类的方法,如果想调用已经被覆盖的基类方法,可以用super(子类名,子类实例对象名).父类方法

class Parent_class:
def Method(self):
print ('父类方法')




class Child_class(Parent_class): # 定义子类
def Method(self):
print ('子类方法')




c = Child_class() # 子类实例化
c.Method() # 子类调用重写方法
super(Child_class,c).Method() #用子类对象调用父类已被覆盖的方法

子类继承父类构造函数

如果在子类中需要父类的构造方法就需要显式地调用父类的构造方法,或者不重写父类的构造方法。

class A:
def __init__(self, x, y):
self.x = x
self.y = y
print('pos is ({0},{1})'.format(self.x, self.y))


def xxx(self):
print('parent now')




class B(A):
def xxx(self):
print('child now')




b = B(10, 3)
b.xxx()

输出

pos is (10,3)
child now


如果重写了__init__ 时,实例化子类,就不会调用父类已经定义的 __init__。

如果重写了__init__ 时,要继承父类的构造方法,可以使用 super 关键字super(子类,self).__init__(参数1,参数2,....),或者父类名称.__init__(self,参数1,参数2,...)


8、类的私有属性

两个下划线开头,声明该属性为私有,像__name不能在类的外部被使用或直接访问。在类内部的方法中使用时 self.__name。


class JustCounter:
__secretCount = 0 # 私有变量
publicCount = 0 # 公开变量


def count(self):
self.__secretCount += 1
self.publicCount += 1
print(self.__secretCount)




counter = JustCounter()
counter.count()
counter.count()
print(counter.publicCount)
print(counter.__secretCount) # 报错,实例不能访问私有变量
Traceback (most recent call last):
File "test.py", line 16, in <module>
print (counter.__secretCount) # 报错,实例不能访问私有变量
AttributeError: 'JustCounter' object has no attribute '__secretCount'


两个下划线开头,声明该方法为私有方法,像__private_method,只能在类的内部调用 ,不能在类的外部调用。self.___private_method。


class Site:
def __init__(self, name, url):
self.name = name # public
self.__url = url # private


def who(self):
print('name : ', self.name)
print('url : ', self.__url)


def __foo(self): # 私有方法
print('这是私有方法')


def foo(self): # 公共方法
print('这是公共方法')
self.__foo()




x = Site('***', 'www.xxx.com')
x.who() # 正常输出
x.foo() # 正常输出
x.__foo() # 报错

输出:

'''
name : ***
url : www.***.com
这是公共方法
这是私有方法
Traceback (most recent call last):
File "F:\Python\Program\test.py", line 61, in <module>
x.__foo() # 报错
AttributeError: 'Site' object has no attribute '__foo'
'''


类的专有方法

__init__ : 构造函数,在生成对象时调用,类似C++构造函数

__del__: 析构函数,释放对象时使用,类似C++析构函数,常用在释放申请的内存空间

__repr__: 打印,转换。这个个函数就是在打印类的时候,控制类输出的字符串


class Name:
def __init__(self, name):
self.name = name




print(Name('s'))


'''
<__main__.Name object at 0x0000023744AFD248>
'''
class Name:
def __init__(self,name):
self.name = name


def __repr__(self): #控制了在打印类时候的输出
return 'Name({!r})'.format(self.name)




print(Name('s'))


'''
Name('s')
'''



__setitem__ : 每当属性被赋值的时候都会调用该方法,因此不能再该方法内赋值 self.name = value 会死循环

__getitem__: 当访问不存在的属性时会调用该方法

__len__: 获得长度,如果一个类表现得像一个list,要获取有多少个元素,就得用len() 函数。要让len()函数工作正常,类必须提供一个特殊方法__len__(),它返回元素的个数。

class CountList:
def __init__(self, *args):
self.list = [x for x in args]
self.count = self.__len__()


def __len__(self):
return len(self.list)


def get_count(self):
return self.count




a = CountList(1, 2, 3, 4, 4, 5)
print(a.get_count())
print(len(a))


__cmp__: 比较运算

__call__: 函数调用

__add__: 加运算

__sub__: 减运算


class MyClass:


def __init__(self, height, weight):
self.height = height
self.weight = weight


# 两个对象的长相加,宽不变.返回一个新的类
def __add__(self, others):
return MyClass(self.height + others.height, self.weight + others.weight)


# 两个对象的宽相减,长不变.返回一个新的类
def __sub__(self, others):
return MyClass(self.height - others.height, self.weight - others.weight)


# 说一下自己的参数
def intro(self):
print("高为", self.height, " 重为", self.weight)




def main():
a = MyClass(height=10, weight=5)
a.intro()


b = MyClass(height=20, weight=10)
b.intro()


c = b - a
c.intro()


d = a + b
d.intro()




if __name__ == '__main__':
main()
'''
高为 10 重为 5
高为 20 重为 10
高为 10 重为 5
高为 30 重为 15
'''

__mul__: 乘运算

__truediv__: 除运算

__mod__: 求余运算

__pow__: 乘方

同样的。类的专有方法也可以重写