编程思想
目前普遍的编程思想有2种:面向过程、面向对象
简单点理解,面向过程就是体现过程,面向对象是针对某一类事物。
比如去饭店吃饭,面向过程就是:客户点单->厨师拿到菜单做菜->服务员上菜->客户吃饭;而面向对象,则将过程中客户、厨师、服务员分为3个不同的对象,客户负责点单、吃饭,厨师负责做菜,服务员负责上菜,每个对象负责自己范围内的事情,将整个流程走通。不像面向过程一样,来多个客户就又有多个过程,面向对象中,对于客户来说,只关心菜有没有(点单)以及上没上,厨师和服务员对客户来说不影响,而对厨师来说,客户相当于不存在,只关心客户产生的订单,所以来了多少客户都没有关系,按照订单上的菜品做菜就行。面向对象就比较方便管理、维护,后续如果有什么流程优化,只需要针对各个对象去优化,而不必像面向过程一样,整个过程去优化和考虑。
类(class):抽象的概念,一类事物。
方法:类中定义的函数,对外提供的服务。
类变量:类变量在整个实例化的对象中是公用的
实例引用:实例化一个对象
实例变量:以‘self.变量名’的方式定义的变量
类定义
# 以“class 类名”的方式来定义类
class ClassName:
xxx
类对象
对象
类一般是通过类名(参数)
来实例化对象,对象可通过对象名.变量名
来访问对象的属性,通过对象名.方法名(参数)
来调用类的实例方法。
class MyClass:
# 在类内部,以“变量名=值”的方式来定义实例变量(属性),实例变量即在通过类实例化成对象时,对象都有该实例变量,并有默认值
var1 = "xxx"
var2 = "xxx2"
# 在类内部,以“def 方法名(self[,参数]): 执行语句”的格式来定义实例方法
def method1(self, param):
print(param)
print(f"实例方法内部调用:{self.var1}, {self.var2}")
# 实例化MyClass类
obj = MyClass()
# 访问实例(对象)的属性和方法
print(obj.var1, obj.var2)
obj.method1("实例方法调用")
以上执行结果为:
xxx xxx2
实例方法调用
实例方法内部调用:xxx, xxx2
构造方法
类中有且仅有一个构造方法__init__()
,该方法在类实例化时会自动调用
class MyClass:
def __init__(self):
print("构造方法")
obj1 = MyClass()
print("---------------")
obj2 = MyClass()
执行结果为:
构造方法
---------------
构造方法
构造方法可以有参数,当构造方法的参数无默认值的时候,实例化对象时,必须传入参数
class MyClass:
var1 = "default"
def __init__(self, var1, var2=45):
self.var1 = var1
print(var1, var2)
obj3 = MyClass(123)
print("---------------")
obj4 = MyClass("离职", 88)
执行结果为
123 45
---------------
离职 88
实例方法的第一个参数
在定义实例方法的时候,第一个参数必须是self
,这个参数表示实例对象自己,在方法中可以通过self.xxx
来调用实例方法和访问对象属性。self代表类的实例,代表当前对象的地址。我们可以通过__class__
来确定当前实例的类
class MyClass:
var = "不知道"
def method1(self, var1):
self.var = var1
print(f"method1方法:{self.var}")
# 实例方法的第一个参数并不一定是self,可以取其他名字,总之是代表对象本身,但是按照惯例,使用self比较好
def method2(xxx, var1):
xxx.var = var1
print(f"method2方法:{xxx.var}")
obj = MyClass()
obj.method1(67)
obj.method2("风扇")
# 直接打印obj变量,是打印出来对象的地址
print(f"对象的地址:{obj}")
# 通过__class__可以获取该对象的类
print(f"对象的类是:{obj.__class__}")
执行结果为:
method1方法:67
method2方法:风扇
<__main__.MyClass object at 0x0000026738003EB0>
<class '__main__.MyClass'>
类变量
类变量是区别于实例变量的说法,类变量是以类名.变量名
的方式访问的,类变量的更新会影响到后续实例化对象,不影响已实例化的对象,实例化对象的更新不影响类变量
class MyClass:
var = "不知道"
def method(self, var1):
self.var = var1
print(f"method1方法:{self.var}")
obj1 = MyClass()
print("---------更新前---------")
# 通过类名.变量名来访问类变量
print(f"类的var:{MyClass.var}")
print(f"对象的var:{obj1.var}")
print("---------对象的var更新---------")
obj1.var = 345
print(f"类的var:{MyClass.var}")
print(f"对象的var:{obj1.var}")
print("---------类的var更新---------")
MyClass.var = 89
print(f"类的var:{MyClass.var}")
print(f"对象的var:{obj1.var}")
print(f"类的var更新,再创建的新对象的var:{MyClass().var}")
执行结果如下:
---------更新前---------
类的var:不知道
对象的var:不知道
---------对象的var更新---------
类的var:不知道
对象的var:345
---------类的var更新---------
类的var:89
对象的var:345
类的var更新,再创建的新对象的var:89
类方法
类方法就是通过类就可以访问的方法,与实例方法不同,实例方法需要通过实例化对象,以对象来调用。
1、类方法的调用为类名.方法名
,对象调用类方法会报错。
2、类方法需要经过@classmethod
来修饰,实例方法不用。
3、类方法的第一个参数一般是cls
(也可以是其他名字),表示该类。实例方法的一个参数表示实例本身。即实例方法是在类实例化的基础上进行使用,必须进行类的实例化,而类方法可以在类、实例上使用。且类方法不能调用该类的实例方法(实例方法需要类实例化)。
4、类方法可以调用类内的类方法和类变量,但是不能调用实例方法和实例变量;实例方法可以调用类方法
class MyClass:
var1 = "value1"
@classmethod
def method1(cls, param1):
cls.var1 = param1
print(MyClass.var1)
MyClass.method1(45)
print(MyClass.var1)
print(MyClass().var1)
执行结果为
value1
45
45
在类方法中,第一个参数代表了当前类,所以有一些比较有趣的用法,可以用该参数去创建实例,示例如下:
class DateFormat:
def __init__(self, year=2022, month=1, day=1):
self.year = year
self.month = month
self.day = day
def out_date(self):
print(f"{self.year}年,{self.month}月,{self.day}日")
@classmethod
def json_format(cls, json_data: dict):
year, month, day = json_data.values()
# 因为cls代表了当前类,所以cls(year, month, day)实际上是:DateFormat(year, month, day),即调用了构造方法(参数与构造方法一致)
return cls(year, month, day)
dd = DateFormat(2022, 5, 19)
print(dd)
dd.out_date()
print("--------------------------")
json_date = {"year": 2022, "month": 8, "day": 28}
demo = DateFormat.json_format(json_date)
print(demo)
demo.out_date()
执行结果为:
<__main__.DateFormat object at 0x000001959DF23D90>
2022年,5月,19日
--------------------------
<__main__.DateFormat object at 0x000001959DF22C20>
2022年,8月,28日
私有属性和私有方法
私有属性以两个下划线__
开头进行修饰,私有属性不能在类的外部被使用或直接访问,在实例方法中使用。
私有方法也是以两个下划线__
开头修饰,私有方法只能在类的内部调用。
class MyClass:
publicVar = "public"
__secretVar = "secret"
def __method1(self):
print("这个是私有方法")
def method2(self):
self.publicVar = "public_chn"
self.__secretVar = "secret_chn"
self.__method1()
mc = MyClass()
print(mc.publicVar)
mc.method2()
print(mc.publicVar)
# __secretVar是私有方法,不能类的外部使用,会报错
print(mc.__secretVar)
执行结果为:
Traceback (most recent call last):
File "E:\hogwart\demo\demo.py", line 17, in <module>
print(mc.__secretVar)
AttributeError: 'MyClass' object has no attribute '__secretVar'
public
这个是私有方法
public_chn
类的专有方法:
__init__ : 构造函数,在生成对象时调用
__del__ : 析构函数,释放对象时使用
__repr__ : 打印,转换
__setitem__ : 按照索引赋值
__getitem__: 按照索引获取值
__len__: 获得长度
__cmp__: 比较运算
__call__: 函数调用
__add__: 加运算
__sub__: 减运算
__mul__: 乘运算
__truediv__: 除运算
__mod__: 求余运算
__pow__: 乘方
继承、多态
继承:类似于子承父业,即定义一个类A1继承了另一个类A,则称A1为A的子类,A为A1的父类。A1默认有A的属性和方法。
class 子类名称(父类名称):
<statement-1>
.
.
.
<statement-N>
子类继承
子类会继承父类的属性和方法
class people:
name = ""
age = 0
__gender = None
def __init__(self, name, age, gender):
self.name = name
self.age = age
self.__gender = gender
def __method1(self):
print("父类:私有方法")
def get_info(self):
self.__method1()
print(f"姓名:{self.name},年龄:{self.age},性别:{self.__gender}")
@classmethod
def method2(cls):
print("父类:类方法")
class student(people):
pass
st = student("张三", 19, "男")
st.get_info()
student.method2()
执行结果为:
父类:私有方法
姓名:张三,年龄:19,性别:男
父类:类方法
子类同样继承了父类的私有方法,比如__init__
构造方法,如果是st = student()
这样调用,会报错
st = student()
TypeError: people.__init__() missing 3 required positional arguments: 'name', 'age', and 'gender'
子类方法重写
如果父类已有的方法不适合子类使用,可以重写父类的方法或者子类自己增加自己使用的方法。
class people:
name = "父类name"
def get_info(self):
print(f"父类的姓名:{self.name}")
class student(people):
name = "子类name"
def get_info(self):
print(f"对父类方法重写,子类的姓名:{self.name}")
def sub_method(self):
print("子类自己的方法")
st = student()
st.get_info()
st.sub_method()
执行结果
对父类方法重写,子类的姓名:子类name
子类自己的方法
多继承
一个类可以继承多个类(有好几个爸o(╥﹏╥)o),即多继承。
在该类实例化后,调用属性或方法时,以多继承时的顺序从左到右搜索,即先看看自己有没有该方法,自己没有,从定义时的第一个父类中找有没有该方法,有就调用该父类的方法,没有就到第二、第三至最后,还未找到就报错。
格式:
class 子类名称(父类1, 父类2, ..., 父类n):
<statement-1>
.
.
.
<statement-N>
示例:
class A:
def method1(self):
print("A类")
def method2(self):
print("A类独有")
class B:
def method1(self):
print("B类")
def method2(self):
print("B类独有")
class C(A, B):
def method1(self):
print("C类")
demo = C()
# method1方法在实例所属的类C中就有,所以就调用自己重写的方法
demo.method1()
# method2方法在实例所属的类C中没有,就依照定义类C的继承的父类顺序找,即从类A->类B这样的顺序,找到调用即止
demo.method2()
执行结果:
C类
A类独有
静态方法
在类中,被@staticmethod
修饰的方法为静态方法,静态方法无法直接使用该类的任何类变量、类方法或者实例方法、实例变量,即没有和类本身有关的参数(包括self、cls等)。静态方法类似于在类中的“函数”。
调用方法为类名.方法名
或实例.方法名
。
class A:
@staticmethod
def method1(param):
print(f"类方法:{param}")
def method2(self):
self.method1("袜子")
print("实例方法")
A.method1("人民币")
a = A()
a.method2()
执行结果:
类方法:人民币
类方法:袜子
实例方法
注意:静态方法不能调用类中的类变量、类方法或实例方法,会报错(这些方法需要类或者实例参数,但是静态方法是没有的),但是类方法和实例方法可以调用静态方法