函数式编程
面向过程编程
面向对象编程:类、实例
一、类定义
1.定义
class Cat(Cat_Dad): # class 类名(父类):
# 大驼峰命名法:每个单词的首字母大写
'''
这是一个猫类
'''
pass
2.概念
- 类(Class): 用来描述具有相同属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法,其中的对象被称作类的实例。是一个独立存放变量(属性/方法)的空间(类只能访问类属性)
- 实例:也称对象。通过类定义的初始化方法,赋予具体的值,成为一个真实的物体。每个实例都是一个独立的变量空间,不同实例之间的空间互相不可见(修改某一实例的类属性时,不影响其他实例)
- 实例化:创建类的实例的过程或操作
- 数据成员:实例变量、类变量、方法、类方法、静态方法和属性等的统称
- 实例变量:定义在实例中的变量,只作用于当前实例
- 类变量:类变量是所有实例公有的变量。类变量定义在类中,但在方法体之外
- 方法:类中定义的函数
- 类方法:类方法是将类本身作为对象进行操作的方法
- 静态方法:不需要实例化就可以由类执行的方法
- 属性:通过实例可以访问的变量。类中每个属性都必须有初始值
- 方法重写:如果从父类继承的方法不能满足子类的需求,可以对父类的方法进行改写,这个过程也称override
- 封装:将内部实现包裹起来,对外透明,提供api接口进行调用的机制
- 继承:即一个派生类(derived class)继承父类(base class)的变量和方法
- 多态:根据对象类型的不同以不同的方式进行处理
- 运算符“.”:用于进行变量空间的运算,通过 . (点)运算符来调用类中封装好的对象
二、 属性
一个“实例”的“特征”,就是“属性”, 例如:color,eat就是我们kitty和hua对象的属性
通过句点法给实例赋值
kitty.color = "white"
kitty.eat = "milk"
hua.color = "colorful"
hua.eat = "fish"
1.实例属性:记录的是每一个实例的特征
class Cat:
'''
这是一个猫类
'''
def __init__(self, color, eat): # init两边分别两个下划线
self.color = color # 实例属性
self.eat = eat
kitty = Cat("white", "milk")
print(kitty.color) # white
#self 指的是实例对象
#color, eat 指的是实例属性
2.类属性:记录与类相关的特征
定义在类中,方法之外的变量,称作类属性
类属性是所有实例公有的变量,每一个实例都可以访问、修改类属性
class Cat:
count = 0 # 类属性,记录你实例化对象的数量
def __init__(self, color, eat):
self.color = color # 实例属性
self.eat = eat
Cat.count += 1 # 内部调用:类名.类属性
kitty = Cat("white", "milk")
hua = Cat("colorful", "fish")
lucky = Cat("black", "food")
#类属性外部调用
print(Cat.count) # 外部调用1:类名.类属性
print(kitty.count) # 外部调用2:实例名.类属性
print(kitty.color) # 实例调用实例属性
#print(Cat.color) # 类是不能调用实例属性
3.私有属性
- 属性前加单下划线
_
:告诉别人这是私有的,但依然可以使用 - 属性前加双下划线
__
:只能在类的内部访问,外部不能使用,但可以蛮横的从外部访问 - 在 Python 中, _ 和 __ 的使用 更多的是一种规范/约定,没有真正限制的目的定义在类中的私有属性也可以被子类继承
(1).用于成员保护和访问限制
class Cat:
def __init__(self, color, eat, old):
self.color = color # 实例属性
self._eat = eat # 外部可访问的私有属性
self.__old = old # 外部不可以访问的私有属性
kitty = Cat("white", "milk", "5")
print(kitty.color) # white
print(kitty._eat) # milk
# print(kitty.__old) # AttributeError: 'Cat' object has no attribute '__old'
(2).外部访问和修改私有属性
class Cat:
def __init__(self, color, eat, old):
self.color = color # 实例属性
self._eat = eat # 外部可访问的私有属性
self.__old = old # 外部不可以访问的私有属性
def get_old(self): # 访问私有属性方法
return self.__old
def set_old(self, old): # 修改私有属性方法
self.__old = old
kitty = Cat("white", "milk", "5")
print(kitty.get_old()) # 5
kitty.set_old(6)
print(kitty.get_old()) # 6
(3).拓展
class Cat:
def __init__(self, color, eat, old):
self.color = color # 实例属性
self._eat = eat # 外部可访问的私有属性
self.__old = old # 外部不可以访问的私有属性
def get_old(self): # 访问私有属性方法
return self.__old
def set_old(self, old): # 修改私有属性方法
self.__old = old
kitty = Cat("white", "milk", "5")
kitty.__old = 6 # 注意:此处并没修改私有属性,而是新建了个叫__old的类属性,所以私有属性值并未改变
print(kitty.get_old()) # 5 # 私有属性
print(kitty.__old) # 6 # 新建的类属性
三、方法
1.实例方法
-
方法总是定义在类中的,但是却叫“实例方法”,因为它表示该类所有实例所共有的行为
-
通常,将默认会传入的那个参数命名为self,用来表示调用这个方法的实例对象本身
-
self:传入的是创建的对象,self可以直接将对象的属性传入到方法中
class Cat:
"""
这是一个猫类
"""
def add_cat(self):
print("{}__{}".format(self.color, self.eat))
kitty = Cat()
kitty.color = "white"
kitty.eat = "milk"
# 函数调用1
Cat.add_cat(kitty) # 类.方法(实例,参数)
# 函数调用2
kitty.add_cat() # 实例.方法(参数)
*********************************************************************
white__milk
white__milk
2.类方法
见描述器与装饰器
3.私有方法
私有方法:不可以被实例和类直接调用,可在函数内部被调用,用于成员保护和访问限制
class Cat:
"""
这是一个猫类
"""
count = 0
def __init__(self, name, color, eat):
self.name = name # 实例属性
self._color = color # 外部可访问的私有属性
self.__eat = eat # 外部不可以访问的私有属性
Cat.count += 1
def __print_cat(self): # 私有方法
return "我是私有方法"
def add_cat(self):
print("{}__{}__{}".format(self.name, self._color, self.__eat))
print(self.__print_cat())
kitty = Cat("kitty", "white", "milk")
Cat.add_cat(kitty)
kitty.add_cat()
# 私有方法:不可以被实例和类直接调用
# kitty.__print_cat()
# Cat.__print_cat()
*********************************************************************
kitty__white__milk
我是私有方法
kitty__white__milk
我是私有方法
四、封装
封装是指将数据与具体操作的实现代码放在某个对象内部,使这些代码的实现细节不被外界发现,外界只能通过接口使用该对象,而不能通过任何形式修改对象内部实现,类通过将函数和变量封装在内部,实现了比函数更高一级的封装
class Cat:
'''
这是一个猫类
'''
count = 0
def __init__(self, color, eat):
self.color = color
self.eat = eat
def add_cat(self):
print("{}__{}".format(self.color, self.eat))
# 错误调用(类将它内部的变量和方法封装起来,阻止外部的直接访问)
# print(count)
# add_cat()
五、继承、多继承
1.概念
-
一个类继承另一个类时,它将自动获得另一个类的所有属性和方法
-
原有的类称为父类,新类称为子类。子类继承了其父类的所有属性和方法,同时还可以定义自己的属性和方法
-
继承的意义是:重用代码,方便代码的管理和修改
-
继承不是变量空间的复制
2.优先级
-
当继承多个父类时,如果父类中有相同的方法,那么子类会优先使用最先被继承的方法
-
类在生成时会自动生成方法解析顺序,可以通过
类名.mro()
来查看
(1)
class A:
def f(self):
print("A")
class B:
def f(self):
print("B")
class C(A):
def f(self):
print("C")
class D(B):
def f(self):
print("D")
class E(C, D):
def f(self):
print("E")
s = E() # 实例化对象
s.f() # 调用方法,self始终指向的是实例对象e,所以方法选择的优先级始终从E类开始
## 优先级 E——>C——>A——>D——>B
print(E.mro())
*********************************************************************
[<class '__main__.E'>, <class '__main__.C'>, <class '__main__.A'>, <class '__main__.D'>, <class '__main__.B'>, <class 'object'>]
(2)
class F:
def f(self):
print("F")
class A(F):
def f(self):
print("A")
class B(F):
def f(self):
print("B")
class C(A):
def f(self):
print("C")
class D(B):
def f(self):
print("D")
class E(C, D):
def f(self):
print("E")
s = E() # 实例化对象
s.f() # 调用方法,self始终指向的是实例对象e,所以方法选择的优先级始终从E类开始
## 优先级 E——>C——>A——>D——>B——>F
3.构造方法
- 子类在调用某个方法或变量的时候,首先在自己内部查找,如果没有找到,则开始根据继承机制在父类里查找
- 根据父类定义中的顺序,以深度优先的方式逐一查找父类
4.重写
当子类继承父类之后,如果子类不想使用父类的方法,可以通过重写来覆盖父类的方法,为此,可在子类中定义一个与要重写的父类方法同名的方法,这样,Python不会考虑父类方法,只关注子类中定义的相关方法
重写后,继续使用父类中的方法的办法
(1) .方法一(不灵活)
class A():
def f(self):
print("A")
class B():
def f(self):
print("B")
class C(A, B):
def f(self):
print("C")
A.f(self) # 调用A.f()
c = C()
c.f()
*********************************************************************
C
A
(2).super().方法名
class A():
def f(self):
print("A")
class B():
def f(self):
print("B")
class C(A, B):
def f(self):
print("C")
super().f() # super().方法名
c = C()
c.f()
*********************************************************************
C
A
5.object基类
object是所有类的父类
6.Mix-in设计模式
“Mix-in类”是继承的终点,多继承就一层,且是最后一层
例如:人(子类)——>胳膊、腿、头、身体(父类)
六、多态
动态语言调用实例方法时不检查类型,只要方法存在,参数正确,就可以调用,这就是多态
class Cat:
def kind(self):
print("i am a cat")
def show_kind(animal):
animal.kind()
c = Cat()
show_kind(c) # i am a cat
七、魔术方法
魔术方法,给了我们一个自定义的接口,函数执行的底层其实就是去调用了魔术方法,我们只需要修改对应的魔术方法,即可实现自定义
1.运算符方法
__add__(self,other) # x+y
__sub__(self,other) # x-y
__mul__(self,other) # x*y
__mod__(self,other) # x%y
__iadd__(self,other) # x+=y
__isub__(self,other) # x-=y
__radd__(self,other) # y+x
__rsub__(self,other) # y-x
__imul__(self,other) # x*=y
__imod__(self,other) # x%=y
例
class Rectangle:
def __init__(self,length,width):
self.length = length
self.width = width
def area(self):
areas = self.length * self.width
return areas
def __add__(self, other):
add_length = self.length + other.length
add_width = self.width + other.width
return add_length,add_width
a = Rectangle(3, 4)
b = Rectangle(5, 6)
print(a + b) # (8, 10)
2.__init__()
实例化方法,通过类创建实例时,自动触发执行。初始化实例属性
__init__
的参数传递过程:实例化产生一个类的实例——>Python自动调用 实例.__init__
(参数)——>转换为类.__init__
(实例,参数)
3.__str__()
、 __repr__()
正常情况下,直接打印类,输出类的地址
为一个类中定义了__str__()
方法,那么在打印对象时,默认输出__str()__
的返回值
class A:
def __str__(self):
return "str"
def __repr__(self):
return "repr"
a = A()
print(a) # str
*********************************************************************
# 把类的实例变成一个字符串,__str__
# __str__:返回的必须是字符串,对用户友好,适合print输出
# __repr__:__str__备胎,如果有__str__,print会先执行__str__
在python中,str和repr方法在处理对象的时候,分别调用的是对象的 __str__
和__repr__
方法
print也是如此,调用str函数来处理输出的对象,如果对象没有定义__str__
方法,则调用repr处理
在 shell 模式下,展示对象 __repr__
的返回值
str:尽可能的提供简洁且有用的信息,让用户尽可能吸收到必要的信息
repr:尽可能向开发者提供创建该对象时的必要信息,让开发者可以直接通过复制粘贴来重建对象
4.__call__()
正常情况下,实例是不能像函数一样被调用的,要想实例能够被调用,就需要定义__call__
方法
为一个类编写了call方法,那么在该类的实例后面加括号,会调用这个方法,即:对象()
或者 类()()
(1).正常情况
class A:
def __init__(self, num):
self.num = num
a = A(33)
a() # TypeError: 'A' object is not callable
(2).call方法
class A:
def __init__(self, num):
self.num = num
def __call__(self):
print("call")
a = A(33)
a() # call
A(33)() # call
5.查看
__module__ # 查看模块名
__class__ # 查看类名
__base__ # 查看继承的父类
__bases__ # 查看继承的全部父类
__dict__ # 查看全部属性,返回属性和属性值键值对形式
__doc__ # 查看对象文档,即类中的注释(用引号注视的部分)
__dir__ # 查看全部属性和方法
例
class A:
"""
这是一个类
"""
def __init__(self, num):
self.num = num
a = A(33)
print(a.__class__)
print(a.__dict__)
print(a.__doc__)
*********************************************************************
<class '__main__.A'>
{'num': 33}
这是一个类
6.__getattr__()
、__setattr__()
、__delattr__()
见描述器与装饰器