简单说下两种思维方式:

【面向过程】就好比自己按照炒菜的步骤亲手制作一道西红柿炒鸡蛋,看中的是解决问题的过程。编程中主要用到的是“函数”知识,将每个过程步骤打包成函数,再依次调用。

【面向对象】是你做个机器人,告诉机器人该怎么做,而面对的这个对象就是机器人。面向对象主要用到的是“类和对象”知识,也是本章的主题。

构造方法

  1. class Dog: # 一般类的命名开头要大写,和变量命名是小写作区分
  2. def __init__(self, name, age, sex='雌性'): # 第一个参数必须是self
  3. self.name = name # 创建和初始化实例变量name
  4. self.age = age
  5. self.sex = sex
  6. d1 = Dog('毛球', 2) # 省略了默认值
  7. d2 = Dog('哈皮', 1, '雄性') # 位置参数赋值
  8. d3 = Dog(name='小米', sex='雄性', age=5) # 关键字参数赋值
  9. print(f"{d1.name}:{d1.age}岁{d1.sex}")
  10. print(f"{d2.name}:{d2.age}岁{d2.sex}")
  11. print(f"{d3.name}:{d3.age}岁{d3.sex}")

实例方法

  1. class Dog:
  2. # todo 构造方法
  3. def __init__(self, name, age, sex='雌性'): # 第一个参数必须是self
  4. self.name = name # 创建和初始化实例变量name
  5. self.age = age
  6. self.sex = sex
  7. # todo 实例方法
  8. def run(self): # 只有一个参数
  9. print(f"{self.name}在跑...")
  10. def speak(self, sound): # 增加一个参数sound
  11. print(f"{self.name}在叫,{sound}!")
  12. dog = Dog('毛球', 2) # 调用构造方法进行传参,省略了默认值
  13. dog.run() # 调用实例方法,使用 对象.实例方法 的形式,不需要传递参数
  14. dog.speak('汪汪汪') # 调用实例方法,使用 对象.实例方法 的形式,传递一个参数sound

类属性:变量

  1. class Account:
  2. rate = 0.38 # 类变量利率rate
  3. def __init__(self, owner, amount):
  4. self.owner = owner # 创建并初始化实例变量账户名
  5. self.amount = amount # 创建并初始化实例变量账户金额
  6. account = Account('Kellen', 800)
  7. print(f"账户名:{account.owner}") # 对实例变量通过 “对象.实例变量” 访问
  8. print(f"账户金额:{account.amount}")
  9. print(f"利率:{Account.rate}") # 对类变量通过 “类名.类变量” 方法

类方法其实也是函数,用法也和函数类似,为什么要单独出一个类呢?两者最大的区别就是:“类”中的函数可以利用“类”中的变量(也就是类方法可以调用类属性),在下面的 @classmethod 中我们会提到。

类方法:函数

类里面除了多个函数,还可以是多个变量。在类中被定义的函数被称为类的【属性】,调用的方式也是一样的 类名.变量名 。

  1. class A():
  2. 变量1 = 100
  3. 变量2 = 200
  4. def 函数1(): # 【不需要】使用类中的变量时,则不需要声明,否则会报错
  5. print('我不需要使用类属性')
  6. @classmethod
  7. def 函数2(cls): # 【需要】使用类属性
  8. print(cls.变量1)
  9. print(cls.变量2)
  10. A.函数1()
  11. A.函数2()

第5行新出现的@classmethod是声明了下面的函数1是类方法,这样才允许函数1使用类中的数据。

第6行的cls是class的缩写,如果类方法函数1想用类属性(也就是类中的变量),就要把cls作为函数1的第一个参数,把类传给自己。

第7,8行,使用 cls.变量名 调用。类方法想使用类属性的时候,需要在这些变量名称前加上cls

最后我们使用【方法】类A,函数() 来调用这个类方法。

以上是使用类方法的三个要点,任何一个格式错误就无法使用。

  1. class Account:
  2. rate = 0.38 # 类变量利率
  3. def __init__(self, owner, amount):
  4. self.owner = owner # 创建并初始化实例变量账户名
  5. self.amount = amount # 创建并初始化实例变量账户金额
  6. # 类方法
  7. @classmethod # 类方法需要装饰器,装饰器是以@开头,修饰函数、方法和类,用来约束它们
  8. def interest_by(cls, amt): # cls代表类自身,即Account类
  9. return cls.rate * amt # cls也可以替换为 Account
  10. interest = Account.interest_by(600.00) # 对类方法可以通过 “类名.类方法” 访问
  11. print(f"计算利息:{interest}") # 对实例变量通过 “对象.实例变量” 访问
  12. print(f"计算利息:{interest:.2f}") # 保留2位小数

给类方法传参

参数可以是外部的,也可以是外部的,使用调用的方式不同。

  1. # 类方法外部传递参数(没有变量) ----------------------------
  2. # 因为只用了外部参数,没有用类属性(也就是变量),所以没有加 @classmethod 和 cls
  3. 一首诗 = ['《卜算子》','我住长江头,','君住长江尾。','日日思君不见君,','共饮长江水。']
  4. class 念诗类():
  5. def 念诗函数(参数):
  6. for i in 参数:
  7. print(i)
  8. 念诗类.念诗函数(一首诗)
  9. # 类方法内部传递参数 ----------------------------
  10. class 念诗类():
  11. 一首诗 = ['《卜算子》','我住长江头,','君住长江尾。','日日思君不见君,','共饮长江水。']
  12. @classmethod # 声明
  13. def 念诗函数(cls): # 赋值
  14. for i in cls.一首诗: # 内部调用
  15. print(i) #打印
  16. 念诗类.念诗函数() #调用
  17. # 类方法同时使用类属性和外部参数 -----------------------
  18. class 念诗类():
  19. 一首诗 = ['《卜算子》','我住长江头,','君住长江尾。','日日思君不见君,','共饮长江水。']
  20. @classmethod # 声明
  21. def 念诗函数(cls,参数): # 增加一个参数值
  22. print('念给'+参数+'的诗:') # 调用参数值
  23. for i in cls.一首诗: # 调用
  24. print(i) # 打印
  25. 念诗类.念诗函数('张三') #调用并把‘张三’赋值给参数

如果我们放到内部,还想调用的话,就要用到我们之前的 @classmethod 三步法:声明、赋值和调用。

外部、内部和两种合并最好放在一起看。最好可以默写出来他们,这样才能理解他们的不同,会默写只是基本,还要理清他们的跳转逻辑,举一反三。

增加/修改类属性(变量)

有两种方式:在外部,用 类.变量 = xx 直接增加/修改类属性;在内部,用 类方法 去增加/修改。

从外部增加/修改属性

  1. 变量1 = 15
  2. #这是类外部的变量1
  3. 变量2 = 'abc'
  4. class A():
  5. 变量1 = 100
  6. #这是类属性变量1
  7. 变量1 = A.变量1 #修改:类属性下面的变量1重新赋值给外部变量1
  8. A.变量2 = 变量2 #新增:类属性下添加变量2,并且等于外部变量2的值
  9. print(类A.变量1)
  10. print(类A.变量2)
  11. # 除了重新赋值,我们还可以通过input()函数来增加/修改变量:
  12. class 类():
  13. @classmethod
  14. def 打印类属性(cls):
  15. print(cls.变量)
  16. 类.变量 = input('请输入字符串:') # 新增类属性(从外部接收一个变量)
  17. 类.打印类属性() # 使用类方法打印出来

我们要区分两个变量,虽然名字相同,但是类外部的【变量1】和类属性【变量1】是两个不同的变量。变量1 = 类A.变量1 是把类属性 100 重新赋值给了类外部的变量1(原来是15),类属性变量1的值没有改变,还是100。

而 类A.变量2 = 变量2,是为类A增加了一个类属性变量2(原来类属性下面只有一个变量1),它的值等于外部的变量2也就是abc。

从内部增加/修改属性

因为用到了内部变量,所以就需要 @classmethod

  1. class 念诗类():
  2. 一首诗 = ['《卜算子》','我住长江头,','君住长江尾。','日日思君不见君,','共饮长江水。']
  3. @classmethod
  4. def 念诗函数(cls):
  5. cls.接收人 = input('请输入你想给谁念诗:')
  6. print('念给'+ cls.接收人 +'的诗:')
  7. for i in cls.一首诗:
  8. print(i)
  9. 念诗类.念诗函数()

我们来对比下他们的区别:

  1. #【从外部增加/修改属性】
  2. class A():
  3. pass
  4. A.变量1 = 100 # 新增一个变量1,也可以使用input()从外部输入
  5. print(类A.变量1) # 打印出来
  6. #【从内部增加/修改属性】
  7. class 类():
  8. @classmethod # 声明类方法
  9. def 增加类属性(cls): # 把cls赋值给方法(增加类属性)
  10. cls.变量 = input('请随意输入字符串:') # 使用input()函数从外部调取变量
  11. 类.增加类属性() # 重新返回变量值给【增加类变量】
  12. print('打印新增的类属性:')
  13. print(类.变量) # 因为有上一步给变量赋了一个值,这一步打印出来。

封装性

如果子类中定义与父类同名的方法或属性,则会自动覆盖父类对应的方法或属性。

私有变量

  1. # 封装-私有变量
  2. class Account:
  3. __rate = 0.38 # 私有类变量利率
  4. def __init__(self, owner, amount):
  5. self.owner = owner # 创建并初始化 实例变量账户
  6. self.__amount = amount # 创建并初始化 私有实例变量余额
  7. def desc(self): # 在类的内部可以访问私有变量
  8. print(f"{self.owner}.金额{self.__amount} 利率{Account.__rate}")
  9. account = Account('Dan', 10000)
  10. account.desc()
  11. print(f'账户名:{account.owner}')
  12. print(f'账户金额:{account.__amount}') # 报错!在类的外部不可以访问私有变量
  13. print(f'利率:{Account.__rate}') # 报错!在类的外部不可以访问私有变量

私有方法

  1. # 封装-私有方法
  2. class Account:
  3. __rate = 0.38 # 私有类变量利率
  4. def __init__(self, owner, amount):
  5. self.owner = owner # 创建并初始化 实例变量账户
  6. self.__amount = amount # 创建并初始化 私有实例变量余额
  7. def __get_info(self): # 定义私有方法
  8. print(f"{self.owner}.金额{self.__amount} 利率{Account.__rate}")
  9. def desc(self): # 在类的内部可以调用私有方法
  10. print(self.__get_info())
  11. account = Account('Tanner', 10000)
  12. account.desc()
  13. account.__get_info() # 报错!在类的外部调用私有方法则会报错

继承性

  1. class Animal: # 定义父类(用于共有的属性)
  2. def __init__(self, name):
  3. self.name = name # 实例变量name
  4. def show_info(self):
  5. return "动物的名字:{0}".format(self.name)
  6. def move(self):
  7. print("动一动...")
  8. class Cat(Animal): # 定义子类并继承父类
  9. def __init__(self, name, age):
  10. super().__init__(name) # 调用父类构造方法,初始化父类成员变量name
  11. self.age = age # 实例化变量age
  12. cat = Cat('Tom', 2)
  13. cat.move()
  14. print(cat.show_info())

多继承

多个父类,调用的子类相同用第一个,只认第一个父类的孩子

  1. # 继承-多继承(多个父类,调用的子类相同用第一个,只认第一个父类的孩子)
  2. class 马: # 定义父类
  3. def __init__(self, name):
  4. self.name = name # 实例变量name
  5. def show_info(self):
  6. return "马的名字:{0}".format(self.name) # 有参数用return
  7. def run(self):
  8. print("马跑...") # 没有参数用print
  9. class 驴: # 定义父类
  10. def __init__(self, name):
  11. self.name = name # 实例变量name
  12. def show_info(self):
  13. return "驴的名字:{0}".format(self.name)
  14. def roll(self):
  15. print("驴打滚...")
  16. class 骡子(马, 驴): # 继承两个父类,从左到右重要性使用
  17. def __init__(self, name, age):
  18. super().__init__(name) # 调用父类构造方法,初始化父类成员变量name
  19. self.age = age # 实例化变量age
  20. m = 骡子('Jaydon', 1)
  21. m.run() # 继承父类马的方法
  22. m.roll() # 继承父类驴的方法
  23. print(m.show_info()) # 两个都有show_info()用第一个马的

方法重写

子类和父类类名相同,用子类

  1. class 马: # 定义父类
  2. def __init__(self, name):
  3. self.name = name # 实例变量name
  4. def show_info(self):
  5. return "马的名字:{0}".format(self.name)
  6. class 骡子(马): # 继承父类马
  7. def __init__(self, name, age):
  8. super().__init__(name)
  9. self.age = age
  10. def show_info(self): # 重写父类方法shwo_info()
  11. return f"骡子{self.name},{self.age}岁"
  12. m = 骡子('Jaydon', 1)
  13. print(m.show_info()) # 子类和父类类名相同,则用子类的