一、面向过程和面向对象区别
面向过程:需要实现一个功能时,着重的是开发的步骤和过程,每个步都需要自己亲力亲为,需要编写代码(自己来做)
面向对象:需要实现一个功能时,不注重的是开发的步骤和过程,关心的是谁来帮我做这件事。
二、类与对象
类是一个抽象的概念,对象是一个实实在在的概念
1、类
类名习惯使用大驼峰命名法:首字母大写,私有类可用一个下划线开头
1)类的定义
class Test:
pass #空类中使用pass
class Test:
food = "chicken"
def info(self): #self指的是创建出来的对象
print(self) #打印当前对象的地址
2)类中包含内容
属性:实际上为类中定义的变量,该变量为属性
方法 :类中定义的函数
3)查看类的属性
Test.__dict__
>> {'__module__': '__main__', 'food': 'chicken', 'info': <function Test.info at 0x0000018F068BD1F0>, '__dict__': <attribute '__dict__' of 'Test' objects>, '__weakref__': <attribute '__weakref__' of 'Test' objects>, '__doc__': None}
4)查看单个属性的内容
Test.__dict__["food"]
>> chicken
5)增删改查类中的单个属性
改:Test.food = "beef"
删:del Test.food
增:Test.drink = "tea"
2、对象
1)创建对象
对象名=类名()
a = Test()
a.info() #此处self为a
2)增删改查对象中的单个属性
改:a.food = "beef"
删:del a.food
增:a.drink = "tea"
3)在方法内通过self获取对象属性
class Test():
food = "chicken"
def info(self): #self指的是创建出来的对象
print(self.food)
a = Test()
a.info()
>> chicken
3、实例属性和类属性的区别
class B:
num = 0 # 类属性
def __init__(self, name):
self.name = name # 实例属性
def test(self):
print(self.name) # 访问实例属性
print(B.num) # 通过类名访问类属性
如果想使用类属性,需要使用:类名.类属性
三、构造函数
def __init__():
__init__()具有初始化作用,当该类被实例化时会自动执行该函数。那么通常可把要初始化的属性放在这个方法里去。
无参构造:def __init__(self):
有参构造:def __init__(self,参数1,参数2...):
构造函数无返回值,一个类中只有一个构造函数
四、析构函数
def __del__(self):
析构函数用于在对象被清除后清除他所占用的内存空间,主要用于对那些长期占用内存的临时变量进行销毁
析构函数没有参数,无返回值,一个类中只有一个析构函数,在跳出对象作用域时会自动调用析构函数。可以在析构函数中加入输出语句,在看到输出语句内容时就表示该对象已被销毁
手动执行析构函数:del 对象名
五、__str__()方法
1、作用
能够在输出对象变量时打印自定义内容。
没有该方法时,输出的是对象的地址;使用该方法时,输出为该方法返回的自定义内容
2、注意
- __str__()必须返回一个字符串;
- 定义了 __str__()方法,在打印对象时,默认输出该方法的返回值
3、例子
class B:
def __str__(self):
return "this is my new name"
b = B()
print(b)
>> this is my new name
六、私有概念
1、定义私有
在python中定义私有变量只需要在变量名或函数名前加上"__"两个下划线,那么这个函数或变量就是私有的了。
2、注意事项
- 类的私有属性和私有方法,都不能通过对象直接访问,但是可以在本类内部访问;
- 类的私有属性和私有方法,不能被子类继承,子类也无法访问;
- 类的私有属性和私有方法,往往用来处理类内部的事情,不通过对象来处理,起到安全作用
3、几种"_"形式
_x:告诉看代码的人这是我定义的私有属性或方法,但在计算机看来就是一个普通属性或方法
__x:真正的私有属性或方法
__x__:用户名字空间的魔法对象或属性
x__:用于避免与Python关键词的冲突
4、访问私有属性
1)不正规方法
对象名._类名__私有属性名
2)正规方法
在类中创建使用私有属性的方法,让对象调用该方法即可
七、继承
继承可以使得子类具有父类的各种属性和方法,而不需要再次编写相同的代码。
1、写法
class 类名(父类名):
pass
2、Python中类的三种定义
1)class A(object): 这种定义任何Python版本均适用,object是所有类的基类
2)class A():
3)class A:
3、继承重写
当父类的方法不能满足子类需求时,可以对方法进行重写。
1)覆盖写:在子类中定义一个和父类同名的方法来实现
2)扩展写:
法一:父类名.方法
class Animal:
def eat(self):
print("吃")
class Dog(Animal):
def eat(self):
Animal.eat(self)
print("啃骨头")
法二:super().方法
class Animal:
def eat(self):
print("吃")
class Dog(Animal):
def eat(self):
super().eat()
print("啃骨头")
4、多继承
1)有多个父类的属性和方法,如果多个父类具有同名方法时候,就近调用(使用A.__mro__方法查看,产生的MRO列表就是一个简单的所有基类的线性顺序列表)
写法:class A(B,C,D):
2)子类从多个父类派生,如果子类有自己的构造函数,则父类的构造函数不会被使用;反之如果子类又没有自己的构造函数,则按顺序继承,哪个父类在最前面且它又有自己的构造函数,就继承它的构造函数;
3)在多继承中如果多个父类有同名方法时,法一可以指定重写某个方法,法二按照MRO列表内容顺序重写对应方法
(以下代码源自Python多继承与super使用详解_python super 多继承-优快云博客)
class Parent(object):
def __init__(self, name, *args, **kwargs): # 为避免多继承报错,使用不定长参数,接受参数
print('parent的init开始被调用')
self.name = name
print('parent的init结束被调用')
class Son1(Parent):
def __init__(self, name, age, *args, **kwargs): # 为避免多继承报错,使用不定长参数,接受参数
print('Son1的init开始被调用')
self.age = age
super().__init__(name, *args, **kwargs) # 为避免多继承报错,使用不定长参数,接受参数
print('Son1的init结束被调用')
class Son2(Parent):
def __init__(self, name, gender, *args, **kwargs): # 为避免多继承报错,使用不定长参数,接受参数
print('Son2的init开始被调用')
self.gender = gender
super().__init__(name, *args, **kwargs) # 为避免多继承报错,使用不定长参数,接受参数
print('Son2的init结束被调用')
class Grandson(Son1, Son2):
def __init__(self, name, age, gender):
print('Grandson的init开始被调用')
# 多继承时,相对于使用类名.__init__方法,要把每个父类全部写一遍
# 而super只用一句话,执行了全部父类的方法,这也是为何多继承需要全部传参的一个原因
# super(Grandson, self).__init__(name, age, gender) 效果和下面的一样
super().__init__(name, age, gender)
print('Grandson的init结束被调用')
print(Grandson.__mro__) # 搜索顺序
gs = Grandson('grandson', 12, '男')
"""
(<class '__main__.Grandson'>, <class '__main__.Son1'>, <class '__main__.Son2'>, <class '__main__.Parent'>, <class 'object'>)
Grandson的init开始被调用
Son1的init开始被调用
Son2的init开始被调用
parent的init开始被调用
parent的init结束被调用
Son2的init结束被调用
Son1的init结束被调用
"""
Grandson的init结束被调用
- super().__init__与类名.__init__,在单继承上用法基本无差
- 但在多继承上有区别,super方法能保证每个父类的方法只会执行一次,而使用类名的方法会导致方法被执行多次
- 多继承时,使用super方法,对父类的传参数,必须把参数全部传递,否则会报错
- 单继承时,使用super方法,则不能全部传递,只能传父类方法所需的参数,否则会报错
- 多继承时,相对于使用类名.__init__方法,要把每个父类全部写一遍, 而使用super方法,只需写一句话便执行了全部父类的方法
八、多态
1、定义
子类重写父类方法,调用不同子类对象的相同父类方法,可以产生不同的执行结果。
2、实现多态的两个前提:
- 继承:多态必须发生在父类与子类之间;
- 重写:子类重写父类方法
九、类方法
1、理解
类方法是定义在类中的方法,通过装饰器@classmethod
来标识。它的第一个参数是cls
(表示类本身),而不是实例对象。类方法可以访问类的属性,并且可以在没有实例的情况下被调用。类方法常用于需要访问类属性或需要在类级别进行操作的场景。
2、定义格式
class 类名:
@classmethod
def 方法名(cls,形参):
方法体
#调用格式
类名.方法名(实参)
3、使用类方法
类方法可以通过类名或实例对象来调用。当类方法被调用时,Python会自动传递类本身作为第一个参数 cls。
class MyClass:
@classmethod
def my_classmethod(cls):
print("Called on class:", cls)
# 通过类名调用
MyClass.my_classmethod()
# 通过实例调用
instance = MyClass()
instance.my_classmethod()
>> Called on class: <class '__main__.MyClass'>
4、使用场景
1)工厂方法:通过定义多个类方法来实现多个构造函数,每个类方法都可以有不同的参数和逻辑,以适应不同的创建对象的场景
class Car:
def __init__(self, make, model):
self.make = make
self.model = model
@classmethod
def from_make_model(cls, make, model):
return cls(make, model)
@classmethod
def from_year(cls, year):
# 假设我们有一个固定的品牌和模型对应于特定的年份
make = "Toyota"
model = f"Corolla {year}"
return cls(make, model)
# 使用不同的工厂方法创建Car实例
car1 = Car.from_make_model("Honda", "Civic")
car2 = Car.from_year(2020)
print(car1.make, car1.model) # 输出: Honda Civic
print(car2.make, car2.model) # 输出: Toyota Corolla 2020
2)在无需实例化的情况下使用类方法访问和修改类级别的属性
class MyClass:
# 类级别的属性
class_attribute = 0
@classmethod
def increment_class_attribute(cls):
# 访问和修改类级别的属性
cls.class_attribute += 1
print(f"Class attribute incremented: {cls.class_attribute}")
@classmethod
def get_class_attribute(cls):
# 访问类级别的属性
return cls.class_attribute
# 调用类方法来操作类级别的属性
MyClass.increment_class_attribute() # 输出: Class attribute incremented: 1
MyClass.increment_class_attribute() # 输出: Class attribute incremented: 2
# 获取类级别的属性值
print(MyClass.get_class_attribute()) # 输出: 2
十、静态方法
1、理解
需要使用装饰器@staticmethod来进行修饰,静态方法既不需要传递类对象也不需要传递实例对象(形参没有self/cls),静态方法也能够通过实例对象和类对象去访问。
2、静态方法和类属性
由于静态方法不会自动接收类(cls
)或实例(self
)作为第一个参数,故它们不能直接访问类的属性或方法,除非这些属性或方法被显式传递给静态方法,或通过类名来访问类级别的属性。
class MyClass:
class_attribute = "这是一个类级别的属性"
@staticmethod
def my_static_method():
# 静态方法不能直接访问类级别的属性
# 下面的代码会引发错误,因为静态方法没有cls参数
# print(cls.class_attribute)
# 但是,静态方法可以通过类名来访问类级别的属性
print(MyClass.class_attribute)
@classmethod
def my_classmethod(cls):
# 类方法可以直接访问类级别的属性
print(cls.class_attribute)
# 调用静态方法和类方法
MyClass.my_static_method() # 输出: 这是一个类级别的属性
MyClass.my_classmethod() # 输出: 这是一个类级别的属性
3、使用场景
- 工具函数:当方法不需要访问对象的任何属性时,它们可以被定义为静态方法。
- 辅助函数:在类中定义一些辅助函数,这些函数与类的功能相关,但不需要访问类或实例的状态。
- 数学计算:数学计算通常不需要访问对象的状态,因此它们可以被定义为静态方法。
十一、类方法,实例方法和静态方法综合应用
class Animal:
# 类属性
species = "Animalia"
def __init__(self, name):
# 实例属性
self.name = name
# 实例方法
def speak(self):
# 直接使用实例属性
print(f"{self.name} makes a noise.")
# 类方法
@classmethod
def get_species(cls):
# 使用类属性
print(f"The species of {cls.__name__} is {cls.species}.")
# 静态方法
@staticmethod
def make_sound():
# 静态方法不使用类或实例属性
print("Some animal makes a sound.")
# 创建Animal类的实例
dog = Animal("Dog")
# 实例方法调用
dog.speak() # 使用对象调用,输出: Dog makes a noise.
Animal.speak(dog) # 使用类调用,需要传参数对象,输出: Dog makes a noise.
# 类方法调用
dog.get_species() # 使用对象调用,输出: The species of Animal is Animalia.
Animal.get_species() # 使用类调用,输出: The species of Animal is Animalia.
# 静态方法调用
dog.make_sound() # 使用对象调用,输出: Some animal makes a sound.
Animal.make_sound() # 使用类调用,输出: Some animal makes a sound.
十二、__new__()方法
1、作用
1)在内存中为对象分配空间
2)返回对象的引用,返回值会引发__init__()调用。
如果不设置,系统选择默认__new__()方法
2、深入理解
没有返回值的__new__()方法不会调用__init__()方法
class MyClass(object):
def __init__(self):
print("This is init")
def __new__(cls, *args, **kwargs):
print("This is new")
# 创建MyClass的实例
obj = MyClass()
print("This is object")
"""
输出:
This is new
This is object
"""
想调用__init__()方法,只需在最后加上返回值
class MyClass(object):
def __init__(self):
print("This is init")
def __new__(cls, *args, **kwargs):
print("This is new")
#法一:
return super().__new__(cls)
#法二:
return object.__new__(cls)
#两种本质均是返回了对象的引用
# 创建MyClass的实例
obj = MyClass()
print("This is object")
"""
输出:
This is new
This is init
This is object
"""
3、__new__()和__init__()方法区别
__new__
方法是真正在创建实例对象时被第一个调用的方法,它负责创建并返回对象的引用。形参为cls- 如果
__new__
方法成功返回了一个对象的引用(即有return),那么__init__
方法随后会被调用,以对这个对象进行初始化。形参为self
十三、单例模式
1、理解
让所有类在实例化时,指向同一个内存地址称之为单例模式
特点:每一次执行类名()返回的对象,内存地址是相同的