python——类
定义
- Python使用class关键字来定义类,class关键字之后是一个空格,然后是类的名字,再然后是一个冒号,最后换行并定义类的内部实现。
- 类名的首字母一般要大写,当然也可以按照自己的习惯定义类名,但一般推荐参考惯例来命名,并在整个系统的设计和实现中保持风格一致,这一点对于团队合作尤其重要。
语法格式:
class ClassName:
#属性
[属性定义体]
#方法
[方法定义体]
#属性就是变量,静态的特征,方法就是一个个的函数,通过这些函数来描述动作行为。
使用
类对象实例化
对象名 = 类名()
>>> car = Car()
注意,类名后面要添加一个括号“()”。
调用对象的属性和方法
对象名.属性名
对象名.方法名
>>> car.infor()
在Python中,可以使用内置方法isinstance()
来测试一个对象是否为某个类的实例。
>>> isinstance(car, Car)
True
>>> isinstance(car, str)
False
构造类
__ init __(self)
只要实例化一个对象的时候,这个方法就会在对象被创建的时候自动调用。实例化对象的时候是可以传入参数的,这些参数会自动传入__ init __(self,param1,param2,…)
方法中,我们可以通过重写这个方法来自定义对象的初始化操作。
举例
>>> class Bear:
def __init__(self,name):
self.name = name
def kill(self):
print("%s,是保护动物,不能杀..."% self.name)
>>> a = Bear('狗熊')
>>> a.kill()
狗熊,是保护动物,不能杀...
另外,我们还可以把传入的参数设置为默认参数,我们在实例化的时候不传入参数系统也不会报错。
>>> class Bear:
def __init__(self,name = "默认的熊"):
self.name = name
def kill(self):
print("%s,是保护动物,不能杀..."% self.name)
self参数
- 类的所有实例方法都必须至少有一个名为self的参数,并且必须是方法的第一个形参(如果有多个形参的话),self参数代表将来要创建的对象本身。
- 在类的实例方法中访问实例属性时需要以self为前缀。
- 在外部通过对象调用对象方法时并不需要传递self参数,如果在外部通过类调用对象方法则需要显式为self参数传值。
- 在Python中,在类中定义实例方法时将第一个参数定义为“self”只是一个习惯,而实际上不必须使用“self”这个名字,尽管如此,建议编写代码时仍以self作为方法的第一个参数名字。
类属性和实例属性
- 在Python中比较特殊的是,可以动态地为自定义类和对象增加或删除属性,这一点是和很多面向对象程序设计语言不同的,也是Python动态类型特点的一种重要体现。
- 实例属性:一般是指在构造函数
__ init __()
中定义的,定义在方法中的变量,属于某个具体的对象,定义和使用时必须以self作为前缀; - 类属性是在类中所有方法之外定义的,在整个实例化的对象中是公用的。
- 在主程序中(或类的外部),实例属性属于实例(对象),只能通过对象名访问;而类属性属于类,可以通过类名或对象名都可以访问。
class Car:
price = 100000 #定义类属性
def __init__(self, c):
self.color = c #定义实例属性
__ dict __
属性
每个类和对象都有一个叫作__ dict __
的字典属性,用来记录该类或对象所拥有的属性。用类名调用输出该由类中所有类属性组成的字典;而使用类的实例对象输出由类中所有实例属性组成的字典。Python内置类型不支持属性的增加,用户自定义类及其对象一般支持属性和方法的增加与删除。
class Car: #通过类调用类属性
price = 100000 #定义类属性
def __init__(self, c):
self.color = c #定义实例属性
>>>car1 = Car("Red")
>>> print(car1.__dict__)
{'color': 'Red'}
实例属性的增删改查
>>> car1 = Car("Red") #实例化对象
>>> car2 = Car("Blue")
>>> print(car1.color) #查看实例属性的值
Red
>>> car1.color = "Yellow" #修改实例属性
>>> print(car2.color) #
Blue
>>> print(car1.color)
Yellow
>>> car1.age=1 #增加实例属性
>>> print(car1.age) #查看实例属性的值
1
>>> del Car.name '#删除类属性
>>> Car.name
Traceback (most recent call last)
私有属性和公有属性
Python并没有对私有属性提供严格的访问保护机制。
- 私有属性:在定义类的属性时,如果属性名以两个下划线“__”或更多下划线开头而不以两个或更多下划线结束则表示是私有属性。
- 私有属性在类的外部不能直接访问,需要通过调用对象的方法来访问,也可以通过Python支持的特殊方式来访问。
- _ xxx:私有属性,只有类对象自己能访问,但在对象外部可以通过“对象名.类名__xxx”这样的特殊方式来访问。
class A:
def __init__(self, value1 = 0, value2 = 0):
self.value1 = value1
self.__value2 = value2
def setValue(self, value1, value2):
self.value1 = value1
self.__value2 = value2
def show(self):
print(self.value1)
print(self.__value2)
def printt(self):
print(self.__value2)
a=A()
a.printt()#内部访问
print(a._A__value2)#外部访问,不能直接访问私有属性
在Python中,以下划线开头的变量名和方法名有特殊的含义,尤其是在类的定义中。
_xxx
:受保护属性,不能用’from module import *'导入;__ xxx __
:系统定义的特殊属性;__ xxx
:私有属性,只有类对象自己能访问,子类对象不能直接访问到这个属性,但在对象外部可以通过“对象名.类名__ xxx
”这样的特殊方式来访问。
class Root:
__total = 0
def __init__(self, v): #构造方法
self.__value = v
Root.__total += 1
def show(self): #普通实例方法
print('self.__value:', self.__value)
print('Root.__total:', Root.__total)
def __private(self): #私有方法
print(‘This is a private function.’)
>>> r._Root__private()
This is a private function.
如果通过类名来调用属于对象的公有方法,需要显式为该方法的self参数传递一个对象名,用来明确指定访问哪个对象的数据属性。
class Root:
__total = 0
def __init__(self, v): #构造方法
self.__value = v
Root.__total += 1
def show(self): #普通实例方法
print('self.__value:', self.__value)
print('Root.__total:', Root.__total)
r = Root(3)
rr = Root(5)
#试图通过类名直接调用实例方法,失败
>>> Root.show()
TypeError: unbound method show() must be called with Root instance as first argument (got nothing instead)
#但是可以通过这种方法来调用方法并访问实例属性
>>> Root.show(r)
self.__value: 3
Root.__total: 2
#通过类名调用实例方法时为self参数显式传递对象名
>>> Root.show(rr)
self.__value: 5
Root.__total: 2
类方法和静态方法
- 类方法:使用装饰器
@classmethod
。第一个参数必须是当前类对象,该参数名一般约定为“cls”
,通过它来传递类的属性和方法(不能传实例的属性和方法), 并且在调用类方法时不需要为该参数传递值。 - 静态方法:使用装饰器
@staticmethod
。可以没有参数,没有“self”和“cls”
参数。实例对象和类对象都可以调用。
class Root:
__total = 0
def __init__(self, v): #构造方法
self.__value = v
Root.__total += 1
def show(self): #普通实例方法
print('self.__value:', self.__value)
print('Root.__total:', Root.__total)
@classmethod #修饰器,声明类方法
def classShowTotal(cls): #类方法
print(cls.__total)
@staticmethod #修饰器,声明静态方法
def staticShowTotal(): #静态方法
print(Root.__total)
>>> r = Root(3)
>>> r.classShowTotal() #对象来调用类方法
1
>>> r.staticShowTotal() #对象来调用静态方法
1
>>> r.show()
self.__value: 3
Root.__total: 1
>>> rr = Root(5)
>>> Root.classShowTotal() #类名调用类方法
2
>>> Root.staticShowTotal() #类名调用静态方法
2
例题
自定义栈,实现基本的入栈、出栈,判栈空,栈满操作。
class Stack:
def __init__(self, size = 10):
'''创建栈对象并进行初始化,默认栈大小为10'''
# 使用列表存放栈的元素
self._content = [] # 初始栈大小
self._size = size # 栈中元素个数初始化为0
self._current = 0
def empty(self):
'''清空栈'''
self._content = []
self._current = 0
def isFull(self):
'''测试栈是否已满'''
return self._current == self._size
def push(self, v):
'''将新元素入栈'''
# 模拟入栈,需要先测试栈是否已满
if self._current < self._size:
self._content.append(v)
# 栈中元素个数加1
self._current = self._current+1
else:
print('Stack Full!')
def pop(self):
'''将栈顶元素出栈'''
# 模拟出栈,需要先测试栈是否为空
if self._content:
# 栈中元素个数减1
self._current = self._current-1
return self._content.pop()
else:
print('Stack is empty!')
构造函数和析构函数
- Python中类的构造函数是
__ init __()
,一般用来为数据属性设置初值或进行其他必要的初始化工作,在创建对象时被自动调用和执行。如果用户没有设计构造函数,Python将提供一个默认的构造函数用来进行必要的初始化工作。 - Python中类的析构函数是
__ del __()
,一般用来释放对象占用的资源,在Python删除对象和收回对象空间时被自动调用和执行。如果用户没有编写析构函数,Python将提供一个默认的析构函数进行必要的清理工作
class Person(object):
def run(self):
print("run")
def eat(self, food):
print("eat" + food)
def __init__(self,name,age,height,weight):
self.name=name
self.age=age
self.height=height
self.weight=weight
def __del__(self):#当程序结束时运行
print("析构函数")
#在函数里定义的对象,会在函数结束时自动释放,这样可以减少内存空间的浪费
>>>per1=Person("lili",20,175,50)
>>>del per1 #手动释放对象
析构函数
>>>print(per1.name)#释放对象后程序不运行
继承机制
- 继承是用来实现代码复用和设计复用的机制,是面向对象程序设计的重要特性之一。设计一个新类时,如果可以继承一个已有的设计良好的类然后进行二次开发,无疑会大幅度减少开发工作量。
- 在继承关系中,已有的、设计好的类称为父类或基类,新设计的类称为子类或派生类。
- Python支持多继承,如果父类中有相同的方法名,而在子类中使用时没有指定父类名,则Python解释器将从左向右按顺序进行搜索。
继承是子类自动共享父类的数据和方法的机制。
语法格式如下:
Class ClassName(BaseClassName):
……
ClassName:是子类的名称,第一个字母必须大写。
BaseClassName:是父类的名称。
派生类可以继承父类的公有属性,但是不能继承其私有属性。如果需要在派生类中调用基类的方法,可以使用内置函数super()或者通过“基类名.方法名()”的方式来实现这一目的。
class A:
#构造方法可能会被派生类继承
def __init__(self):
self.__private()
self.public()
#私有方法在派生类中不能直接访问
def __private(self):
print('__private() method in A')
#公开方法在派生类中可以直接访问,及被覆盖
def public(self):
print('public() method in A')
class B(A):
#类B没有构造方法,会继承基类的构造方法
def __private(self): #不会覆盖基类的私有方法
print('__private() method in B')
#覆盖了继承自A类的公开方法public
def public(self):
print('public() method in B')
>>> b = B() #自动调用基类构造方法
__private() method in A
public() method in B
>>> dir(b) #基类和派生类的私有方法访问方式不一样
['_A__private', '_B__private',...]
>>> b._A__private()
__private() method in A
#dir( ):不仅仅输出本类中新添加的属性名和方法,还会输出从父类继承得到的属性名和方法名。
>>> class C(A):
def __init__(self): #显式定义构造函数
self.__private() #这里调用的是类C的私有方法
self.public()
def __private(self):
print('__private() method in C')
def public(self):
print('public() method in C')
>>> c = C() #调用类C的构造方法
__private() method in C
public() method in C
>>> dir(c)
['_A__private', '_C__private', '__class__', ...]
多态
- •所谓多态(polymorphism),是指基类的同一个方法在不同派生类对象中具有不同的表现和行为。派生类继承了基类行为和属性之后,还会增加某些特定的行为和属性,同时还可能会对继承来的某些行为进行一定的改变,这都是多态的表现形式。
- Python大多数运算符可以作用于多种不同类型的操作数,并且对于不同类型的操作数往往有不同的表现,这本身就是多态,是通过特殊方法与运算符重载实现的。
>>> class Animal(object): #定义基类
def show(self):
print('I am an animal.')
>>> class Cat(Animal): #派生类,覆盖了基类的show()方法
def show(self):
print('I am a cat.')
>>> class Dog(Animal): #派生类
def show(self):
print('I am a dog.')
>>> class Tiger(Animal): #派生类
def show(self):
print('I am a tiger.')
>>> class Test(Animal): #派生类,没有覆盖基类的show()方法
pass
>>> x = [item() for item in (Animal, Cat, Dog, Tiger, Test)]
>>> for item in x: #遍历基类和派生类对象并调用show()方法
item.show()
I am an animal.
I am a cat.
I am a dog.
I am a tiger.
I am an animal.