面向对象的程序设计(Object Oriented Programming, OOP)的一个关键性的观念就是将数据以及对数据的操作封装在一起, 组成一个相互依存、不可分割的整体, 即对象Object 。 对于相同的类型的对象抽象后得到共同的特征 就形成了类Class。
内置的数据类型列表,字典,元组等等都是类, 学习python必须熟悉了解类,下面从相关的概念出发,对类做一些梳理。

1 属性和方法

类就是把数据以及对数据的操作方法放在一起, 数据无非就是变量, 对数据的操作方法无非就是函数。 把这两种统称为类的成员;而函数称为成员方法 ,变量称为成员属性

2 实例属性和类属性

成员属性分为 实例属性和类属性,

  • 实例属性一般指在构造函数__init__ 中定义的,定义和使用时必须以self作为前缀
  • 类属性是在类中所有方法之外定义的变量
    实例属性是由实例决定的, 不同的实例可以是不同的实例属性; 而类属性是全局的,每个实例的类属性都相同;(实例属性与类属性的关系 像 局部变量与全局变量的关系)
class Car: 
    price = 10000  # 这是类属性, 定义在所有方法之外
    def __init__(self, c): 
        self.color = c   # 这是实例属性

由于类属性是全局的, 所以通过类.属性 和 实例.属性 都可以访问, 而实例属性只可以通过实例.属性 访问

car1 = Car('red') #创建一个对象, 也叫做类的实例化 ,从人类 到具体的 “张三”
Car.price  #类.属性访问
>>>10000
car1.price  #实例.属性访问
>>>10000
Car.color 
>>>AttributeError: type object 'Car' has no attribute 'color' # 实例属性不可以通过 类.属性 访问
car1.color
>>>'red'
isinstance(car1, Car)  #  查看一个对象是否为某个类的实例
>>> True

在类定义完成之后, python 可以动态的 新增或者修改成员 ,如下

Car.company = "Volkswagen"  # 增加成员属性 
Car.price = 11111  # 修改成员属性 
def setSpeed(self, s): 
    self.speed = s 
import  types 
Car.setSpeed  = types.MethodType(setSpeed, car1) # 增加成员方法

方法和函数的区别在于, 方法一般指与实例相关的函数, 调用方法时, 实例本身会作为第一个参数传递, 所以在增加成员方法时, 要调用 types.MethodType() 将函数转化为方法, 然后才能添加到类。

属性

属性和方法一个是变量一个是函数,调用的时候差一个括号, 也可以将方法属性化, 不加括号就能以调用

class Car: 
    price = 10000  
    def __init__(self, c): 
        self.color = c   
    def showcolor1(self):
        return self.color
        
    @property    # 属性装饰器
    def showcolor2(self):
        return self.color
car = Car('red')
car.price   # 访问属性
>>>10000
car.showcolor1() # 调用方法 
>>>'red'
car.showcolor2   # 方法属性化 
>>>'red'

dir(class_name)查看类中方法和属性, help(class.method) 查看方法说明

dir(car) 
['__class__',
 '__delattr__',
 '__dict__',
 '__dir__',
 '__doc__',
 '__eq__',
 '__format__',
 '__ge__',
 '__getattribute__',
 '__gt__',
 '__hash__',
 '__init__',
 '__init_subclass__',
 '__le__',
 '__lt__',
 '__module__',
 '__ne__',
 '__new__',
 '__reduce__',
 '__reduce_ex__',
 '__repr__',
 '__setattr__',
 '__sizeof__',
 '__str__',
 '__subclasshook__',
 '__weakref__',
 'color',
 'price',
 'showcolor1',
 'showcolor2']

3 共有成员 与 私有成员

为了数据的保密性,需要为某些变量设定成私密属性,使得从类的外部不可以直接方法, 即私有成员; 与之相对的即公有成员。

  • __x ,python 中双下划线开头的即为私有成员
  • _x,单下划线开头为保护成员, 不可以使用import 导入, 只有类内部或者子类可以使用
  • x , 两边都用下划线的为特殊成员。
class Car: 
    __price = 10000  
    def __init__(self, c): 
        self.color = c  
car = Car('red')
Car.__price
AttributeError: type object 'Car' has no attribute '__price'
car.__price
AttributeError: type object 'Car' has no attribute '__price'  # 不管是实例还是类都不能访问私有成员

但是为了程序的测试和调试, python提供了一种方法可以在类外部访问私有成员, “实例._类__私有属性”

car = Car('red')  
car._Car__price
>>>10000   # 可以访问

4 公有方法、私有方法、静态方法、类方法

公有方法和私有方法都属于实例(第一个参数是self), 每个实例都有自己的公有方法和私有方法, 但私有方法不可以通过 实例.私有方法直接调用,只能在内部调用(或者通过python的特殊方式调用)。

class Car: 
    def __init__(self, c): 
        self.color = c 
    def __show(self):    #  私有方法 
        print(self.color)
    def setColor(self, newcolor):  # 公有方法 
        self.color = newcolor 
        self.__show()
car1 = Car('red') 
car1.__show()
>>>AttributeError: 'Car' object has no attribute '__show'
car1.setColor("bule")
>>>bule

可以看到对于私有方法__show() ,从外部调用时它给你藏着掖着, 骗你说没有, 但是类内部公有方法setColor()可以通过 self.__show() 调用。
静态方法和类方法可以通过类名和实例名调用, 但不能直接访问实例成员,只能访问类成员,都是总体的方法。

class Car: 
    price = 10000
    def __init__(self, c): 
        self.color = c   # 这是实例属性 
    @classmethod   #类方法修饰器
    def showprice(cls): 
        print(cls.price)
    @staticmethod  # 静态方法修饰器
    def sayhello(): 
        print('hello')   
car1 = Car('red') 
car1.showprice()
car1.sayhello()
>>>10000
>>>hello

注意: 类方法和静态方法属于是不依赖于实例的, 不可以调用实例成员

运算符重载

为什么int 类的加法和列表的加法都是同样的符号“+” ,但运算方法却不同,这就是运算符的重载,对于同一个运算符,对于不同的类定义的计算方法。
例如,对于Car类,将两辆车“+”定义为其价格之和,重载__add__

class Car: 
    def __init__(self, color, money): 
        self.color = color
        self.money = money
    def __add__(self, anothercar):
        if isinstance(anothercar, Car): 
            return self.money + anothercar.money
        else : print('this is nor a car ')
car1 = Car("red", 10000)
car2 = Car("bule",10000)
car1 + car2
>>>20000

继承

继承是为了复用而设计的,当需要一个新类时, 如果新类是某个设计好的类的子类, 那么直接调用设计好的类不失为一个机智高效的方法,分别称为子类和父类。 这种行为就是继承。
比如现在需要一个 大众汽车类,可以继承Car类功能,

class Volkswagen(Car): 
    def setmodel(self, m): 
        self.model = m 
car3 = Volkswagen( 'yellow', 100)
car3.color
>>>'yellow'
car3.money
>>>100    # 继承了负类的成员 
car3.setmodel('迈腾')
>>>car3.model 
'迈腾'

查看一个类的子类 可以用 issubclass(son, father) .或者 class.__bases__查看其父类。

issubclass(Volkswagen, Car) 
True

Volkswagen.__bases__
(__main__.Car,)