python面向对象
1.类和对象的定义
类是一种用户自定义的数据类型,它是由数据和方法组成.数据表示属性,方法表示行为.
一个类可以包含多种属性和方法.属性是类的成员变量,可以存储数据.方法是一组操作数据的代码,他们可以实现某些功能或者改变属性的值.
class 类名: 类属性 = 值 def __init__(self, 参数): self.属性1 = 参数1 self.属性2 = 参数2 def 方法1(self, 参数): # 方法代码 def 方法2(self, 参数): # 方法代码
class是声明类的关键字.
__init__
是类的构造方法,当创建类的对象时,会自动调用这个方法,self表示对象本身,其它参数就是对象的属性.
属性是类的成员变量,可以在类的内部和外部使用.可以在类外使用类名访问属性, 如:类名.类属性
方法是类的成员函数.接受的第一个参数是self,表示对象本身.在类内使用方法时,不需要传递这个参数.外部访问方法时,需要在对象后面加上方法名. 如: 对象.方法名()
class Dog: # 类属性 species = '犬科' # 构造方法 def __init__(self, name, age): self.name = name self.age = age # 方法 def bark(self): print("汪汪汪") # 创建对象 dog1 = Dog('大黄', 2) dog2 = Dog('二黄', 1) # 访问属性 print(dog1.name) print(dog2.age) # 调用方法 dog1.bark()
python的__init__
详解
介绍__init__
方法的作用
__init__
方法是 Python 中面向对象编程中类的特殊方法,也称为构造方法,当创建一个类的实例时,__init__
方法会自动调用。
它的主要作用是初始化实例的属性,在实例被创建后,你可以通过这些属性对实例进行操作。每个类可以定义多个不同的__init__
方法,但通常情况下,在类中只有一个,在这种情况下,在创建类的实例时,必须提供所需的参数。
讲解__init__
方法的语法
def __init__(self, 参数1, 参数2, ...): 初始化语句1 初始化语句2 ...
self 参数是必须的,它代表创建的对象本身,在方法内部可以通过 self 来引用对象的属性和方法。除了 self 以外的其他参数是可选的,根据实际需求定义。
初始化语句就是在创建对象时需要执行的语句,可以是赋值语句、函数调用等。通过 __init
方法,可以在创建对象时为对象的属性设置初始值,从而使得代码更加简洁,也方便维护。
如何在类中使用__init__
方法初始化类的属性
class Dog: def __init__(self, breed, name, age): self.breed = breed self.name = name self.age = age dog = Dog("Labrador", "Max", 3) print(dog.breed) # Output: Labrador print(dog.name) # Output: Max print(dog.age) # Output: 3
创建了一个名为 Dog 的类,并且定义了 init 方法。 init 方法接收三个参数:breed, name 和 age,并且通过 self. 前缀来设置类的属性。最后,我们创建了一个名为 dog 的对象,并且初始化了它的 breed, name 和 age 属性。
如何使用__init__
方法给对象动态添加属性
在python中,shiyong__init__
方法可以动态地给对象添加属性.在定义__init__
方法时,可以接受任意的参数,然后使用这些参数来初始化类的属性,如果你想给类的每个对象都添加一个名字属性,可以这样定义__init__
方法:
class person: def `__init__`(self,name): self.name = name person = Person("john Doe") print(person.name)
__init__
方法的多态特性
__init__
方法也支持多态的特性.这意味着,不同的子类可以通过定义自己的__init__
方法来覆盖父类的__init__
方法,以实现不同的初始化行为.
class Person: def `__init__`(self , name , major): self.name = name self.age = age class Student(Person): def `__init__`(self,name,age,major): Person.`__init__`(self,name,age) self.major = major
子类Student继承了Person类,并定义了自己的__init__
方法.当创建Student类的对象时,将调用子类的__init__
方法.而不是父类__init__
方法.
多态可以为不同子类提供不同的初始化行为,从而更灵活地管理类的属性.
__init__
和super的用法
__init__
方法是python中类的构造方法,在创建类的实例时被调用,用于初始化类的实例的属性.
super是python的内置函数,它可以继承关系中访问父类的属性,在父类和子类中都有定义了__init__
方法的情况下,子类的__init__
方法可以通过调用super函数来继承父类__init__
方法,并添加额外的初始化代码.
class ParentClass: def __init__(self, value1, value2): self.value1 = value1 self.value2 = value2 class ChildClass(ParentClass): def __init__(self, value1, value2, value3): super().__init__(value1, value2) self.value3 = value3
ChildClass 继承了 ParentClass 的所有属性,并且在其中添加了额外的 value3 属性。调用 super().__init__
(value1, value2) 可以访问父类的__init__
方法,并将其初始化为 value1 和 value2。
注意:
-
__init__
方法在对象创建时自动调用,无需手动调用. -
__init__
方法可以接受任意数量的参数,但必须遵循特定的参数签名. -
在
__init__
方法中,必须给对象的每一个属性赋值,否则该对象将不完整,不能正常工作. -
可以在
__init__
方法中调用其他方法,但不要在初始化的过程中产生太多的计算,因为这可能会影响程序的性能. -
一个类可以没有
__init__
方法,程序会以默认方式创建该类的对象.
封装,继承和多态的概念和应用
封装
封装: 是指将数据和行为打包到一个类中,并可以控制外部访问的级别. 封装可以保护数据和方法的访问,并且可以提高代码的可维护性.可以利用访问控制修饰符来实现封装,包括公有,私有,受保护和包访问等四种级别.
class Animal: def __init__(self, name): self.name = name def eat(self): print(self.name + '在吃东西') class Dog(Animal): def __init__(self, name): super().__init__(name) def bark(self): print(self.name + '在汪汪叫') dog = Dog('小狗') dog.eat() dog.bark()
继承
继承是指一个类可以从父类中继承方法和属性.如果父类的某个属性或方法也在子类中定义了,那么子类会重写父类的属性和方法,这样子类可以更好地针对自己的特定要求来实现相应的功能.
class Animal: def __init__(self,name): self.name = name def eat(self): print(self.name + '在吃东西') class Dog(Animal): def __init__(self,name): super().__init__(name) def bark(self): print(self.name + '在汪汪叫') dog = Dog('小狗') dog.eat() dog.bark()
多态
多态是指对象可以用多种形态来引用.这样做可以使代码更加灵活,因为同样的操作可以应用于不同的类型.多态有两种实现方法,一种是基于继承的实现,在这种实现中,父类定义一些通用的方法,子类则可以重写这些方法并实现不同的功能.另一种实现方法是接口,这种实现方法可以让不同的类实现同一接口,从而实现多态.
class Animal: def move(self): pass class Dog(Animal): def move(self): print("狗在跑") class Cat(Animal): def move(self): print("猫在爬") class Zoo: def __init__(self): self.animals = [] def addAnimal(self,animal): self.animals.append(animal) def moveAll(self): for animal in self.animals: animal.move() zoo = Zoo() zoo.addAnimal(Dog()) zoo.addAnimal(Cat()) zoo.moveAll()
if name == 'main'
通俗的理解__name__ == '__main__'
:假如你叫小明.py,在朋友眼中,你是小明(__name__ == '小明')
;在你自己眼中,你是你自己(__name__ == '__main__')
。
if __name__ == '__main__'
的意思是:当.py文件被直接运行时,if name == 'main'之下的代码块将被运行;当.py文件以模块形式被导入时,if name == 'main'之下的代码块不被运行。
init与self
⭐Python中self的含义
self,英文单词意思很明显,表示自己,本身。python的self,是个对象(Object),是当前类的实例。
⭐Python中为何要有self
那就是:
在类的代码(函数)中,需要访问当前的实例中的变量和函数的,即,访问Instance中的:
-
对应的变量(属性,property):Instance.ProperyNam,去读取之前的值和写入新的值
-
调用对应函数(function):Instance.function(),即执行对应的动作
-> 而需要访问实例的变量和调用实例的函数,当然需要对应的实例Instance对象本身
-> 而Python中就规定好了,函数的第一个参数,就必须是实例对象本身,并且建议,约定俗成,把其名字写为self
-> 所以,我们需要self
而如果没有用到self,即代码中,去掉self后,那种写法所使用到的变量,实际上不是你所希望的,不是真正的实例中的变量和函数,而是的访问到了其他部分的变量和函数了。甚至会由于没有合适的初始化实例变量,而导致后续无法访问的错误。
下面,就通过代码,来演示,如果去掉self,或者没有合理的使用self,会出现哪些错误。
⭐首先来看一下init()和self对象
#!/usr/bin/env python # -*- coding:utf-8 -*- # Author: antcolonies class Person(object): def __init__(self, name, lang, website): self.name = name self.lang = lang self.website = website print('self: ', self) print('type of self: ', type(self)) ''' 未实例化时,运行程序,构造方法没有运行 ''' p = Person('Tim', 'English', 'www.universal.com') '''实例化后运行的结果 self: <__main__.Person object at 0x00000000021EAF98> type of self: <class '__main__.Person'> '''
可以看出self为实例变量p,是一个Person类型的对象。
class Dog(object): def __init__(self,name,dog_type): self.name = name self.type = dog_type def sayhi(self): print("hello,I am a dog, my name is ",self.name) d = Dog('LiChuang',"京巴") # 实例化 d.sayhi()
以下是d = Dog('LiChuang',"京巴")实例化的示意图:
⭐如果没有在init中初始化对应的实例变量的话,导致后续引用实例变量会出错
如下代码,完整的演示了,如果没有在类Class的最初的init函数中,正确的初始化实例变量,则会导致后续没有变量可用,因而出现AttributeError的错误:
#!/usr/bin/env python # -*- coding:utf-8 -*- # Author: antcolonies name = 'whole global name' ''' 注:此处全局的变量名,写成name,只是为了演示而用 实际上,好的编程风格,应该写成gName之类的名字, 以表示该变量是Global的变量 ''' class Person(object): def __init__(self, newPersonName): # self.name = newPersonName ''' 如果此处不写成self.name 那么此处的name,只是__init__函数中的局部临时变量name而已 和全局中的name,没有半毛钱关系 ''' name = newPersonName ''' 此处只是为了代码演示,而使用了局部变量name, 不过需要注意的是,此处很明显,由于接下来的代码也没有利用到此处的局部变量name 则就导致了,此处的name变量,实际上被浪费了,根本没有利用到 ''' def sayYourName(self): ''' 此处由于找不到实例中的name变量,所以会报错: AttributeError: Person instance has no attribute 'name' ''' print('My name is %s' %self.name) def selfAndInitDemo(): personInstance = Person('Tim') personInstance.sayYourName() if __name__ == '__main__': selfAndInitDemo() ''' 未使用self.name时抛异常 Traceback (most recent call last): File "E:/python14_workspace/s14/day06/test_1.py", line 18, in <module> selfAndInitDemo() File "E:/python14_workspace/s14/day06/test_1.py", line 15, in selfAndInitDemo personInstance.sayYourName() File "E:/python14_workspace/s14/day06/test_1.py", line 11, in sayYourName print('My name is %s' %self.name) AttributeError: 'Person' object has no attribute 'name' '''
从上述代码可见,由于在类的初始化(实例化)的init函数中,没有给self.name设置值,使得实例中,根本没有name这个变量,导致后续再去访问self.name,就会出现AttributeError的错误了。
对应的,如果写成self.name,则意思就正确了,就是初始化的时候,给实例中新增加,并且正常设置了正确的值newPersionName了,所以后续再去通过self.name,就可以访问到,当前实例中正确的变量name了。
相应的正确写法的代码如下:
#!/usr/bin/env python # -*- coding:utf-8 -*- # Author: antcolonies name = 'whole global name' ''' 注:此处全局的变量名,写成name,只是为了演示而用 实际上,好的编程风格,应该写成gName之类的名字, 以表示该变量是Global的变量 ''' class Person(object): def __init__(self, newPersonName): self.name = newPersonName ''' 此处正确的,通过访问self.name的形式,实现了: 1.给实例中,增加了name变量 2.并且给name赋了初值,为newPersionName ''' def sayYourName(self): ''' 此处由于开始正确的初始化了self对象,使得其中有了name变量, 所以此处可以正确访问了name值了 ''' print('My name is %s' %self.name) def selfAndInitDemo(): personInstance = Person('Tim') personInstance.sayYourName() if __name__ == '__main__': selfAndInitDemo() '''My name is Tim'''
⭐在函数中,使用对应的变量,虽然代码是可以运行的,但是实际上却是使用的,不是实例中的变量
有时候,虽然你写的代码,可以运行,但是使用到的变量,由于没有加self,实际上是用到的不是实例的变量,而是其他的变量。
此类问题,主要和Python中的变量的作用域有关,但是此处例子中,也和是否使用self有关:
#!/usr/bin/env python # -*- coding:utf-8 -*- # Author: antcolonies name = 'whole global name' ''' 注:此处全局的变量名,写成name,只是为了演示而用 实际上,好的编程风格,应该写成gName之类的名字, 以表示该变量是Global的变量 ''' class Person(object): name = 'class global name' def __init__(self, newPersonName): # self.name = newPersonName ''' 此处,没有使用self.name 而使得此处的name,实际上仍是局部变量name 虽然此处赋值了,但是后面没有被利用到,属于被浪费了的局部变量name ''' name = newPersonName def sayYourName(self): ''' 此处,之所以没有像之前一样出现: AttributeError: Person instance has no attribute 'name' 那是因为,虽然当前的实例self中,没有在__init__中初始化对应的name变量,实例self中没有对应的name变量 但是由于实例所对应的类Person,有对应的name变量,所以也是可以正常执行代码的 对应的,此处的self.name,实际上是Person.name ''' print('My name is %s' %self.name) print('Name within class Person is actually the global name: %s' %name) print("Only access Person's name via Person.name = %s" %(Person.name)) def selfAndInitDemo(): personInstance = Person('Tim') personInstance.sayYourName() print('whole global name is %s' %name) if __name__ == '__main__': selfAndInitDemo() ''' My name is class global name Name within class Person is actually the global name: whole global name Only access Person's name via Person.name = class global name whole global name is whole global name '''
其中,可见,此处开始init中,没有给self实例初始化对应的name,
而后面的函数sayYourName中,虽然可以调用到self.name而没有出现AttributeError错误,
但是实际上此处的值,不是所期望的,传入的name,即"Tim",而是类中的name的值,即"class global name"。
python中的面向对象编程基础
1、创建类和对象
首先,类是我们在面向对象编程中的基础,它是一种用来描述具有相同属性和方法的对象集合的蓝图。举个例子,我们可以创建一个名为 "人" 的类,这个类里面包含了姓名、年龄、性别等属性,以及 eat、sleep、work 等方法。然后我们可以实例化这个 "人" 类,创建很多具有不同属性的人的对象。
代码示例:
首先,我们来创建一个“人”类
class Person: # 把属性放在 init 方法里面,注意第一个参数永远是 self,代表该类的实例 def init(self, name, age, gender): self.name = name self.age = age self.gender = gender # 这里是一个日常生活中的行为,写成方法 def eat(self): print("{} 在吃饭".format(self.name))
# 再来一个睡觉 def sleep(self): print("{} 正在睡觉".format(self.name)) # 最后一个工作 def work(self): print("{} 在工作".format(self.name))
现在我们创建两个人的对象
p1 = Person("小明", 18, "男") p2 = Person("小红", 22, "女")
通过对象调用方法
p1.eat() # 小明 在吃饭 p2.sleep() # 小红 正在睡觉 我们创建了一个名为 "Person" 的类,并定义了 "name"、 "age"、 "gender" 等属性,然后定义了 "eat"、 "sleep"、 "work" 等方法。通过实例化这个类,我们可以创建不同名字、不同年龄、不同性别的人,来调用这些方法。
2、初始化方法和实例属性
初始化方法是在创建一个新的对象实例时,执行其中的代码,以初始化对象的属性和状态。Python 中的初始化方法通常为 init(),它是所有类中的可选方法。当您创建一个类的新实例时,Python 将自动调用 init() 方法,并将该实例作为第一个参数传递给它,并使用该实例来设置各种属性和状态。
代码示例:
首先,我们定义了一个名为 Person 的类,并在其中编写了初始化方法 init():
class Person: def init(self, name, age): self.name = name self.age = age 在上面的代码中,我们定义了一个新的类 Person,并在其中定义了初始化方法 init()。此方法将 name 和 age 作为参数传递,并使用 self.name 和 self.age 对象属性分别赋值。
接下来创建一个 Person 实例,并访问其中的属性:
person1 = Person("小明", 20) print("这个人的名字是:", person1.name) print("这个人的年龄是:", person1.age)
输出结果是:
这个人的名字是:小明
这个人的年龄是:20
在 Python 中,实例属性是方法内部定义的属性,它们与类的实例相关联,并且每个实例都有自己的一套实例属性。
代码示例:
我定义了一个名为 Car 的类,并为每个实例定义了一组属性:
class Car: def init(self): self.color = "黑色" self.brand = "奥迪" self.year = 2023
def describe(self): print("这辆车是一辆 %d 年的 %s %s ,颜色为 %s" % (self.year, self.brand, self.model, self.color))
car1 = Car() car1.model = "Q7" # 为对象手动添加一个属性 car1.describe()
输出结果是:
这辆车是一辆 2023 年的 奥迪 Q7 ,颜色为 黑色
在上面的代码中,我们定义了一个新的类 Car,并在其中编写了初始化方法 init()。该方法设置了一个名为 color、brand 和 year 的实例属性。同时,该类还定义了一个名为 describe() 的方法,该方法将输出具体的汽车属性。
当我们创建一个 Car 实例 car1 时,Python 将自动调用 init() 方法,并设置其 color、brand 和 year 实例属性。接着我们又手动添加了一个 model 实例属性,并调用了 describe() 方法,从而输出了 car1 实例的详细属性。
3、类方法和静态方法的应用
在Python中,类方法和静态方法都是用来处理类的一些数据和行为的。两种方法都是通过类名进行调用,而不是通过实例对象进行调用。
一、类方法
在Python中,定义类方法需要使用‘@classmethod’装饰器。类方法的第一个参数必须是‘cls’,表示类本身,可以通过‘cls’来调用类属性和类方法。
代码示例:
class Person: total_persons = 0
def __init__(self, name): self.name = name Person.total_persons += 1 @classmethod def show_total_persons(cls): print("Total persons: ", cls.total_persons)
p1 = Person("Tom") p2 = Person("Jerry") Person.show_total_persons() 在上面的代码中,我们创建了一个‘Person’类来记录创建的总人数。我们定义一个类方法‘show_total_persons()’来显示总人数。在实例化两个‘Person’对象后,我们使用‘Person.show_total_persons()’来显示人数。
二、静态方法
在Python中,定义静态方法需要使用‘@staticmethod’装饰器。静态方法没有参数限制,它只是一个普通函数,涉及到类和对象的问题,都需要在函数内部进行处理。
代码示例:
class Calculator:
@staticmethod def add(x, y): return x + y
Calculator.add(3, 5) 在上面的代码中,我们定义了一个静态方法‘add()’,然后在类名后面直接调用它。
三、类方法和静态方法的应用
在Python中,类方法和静态方法的应用非常广泛,可以用来处理一些通用的类方法和静态方法。
代码示例:
在我们进行日期计算的时候,经常会用到类似的代码:
from datetime import datetime
now = datetime.now() current_year = now.year 但是这种代码重复了很多次,我们可以定义一个类方法来处理它。
class DateCalculator:
@classmethod def current_year(cls): now = datetime.now() return now.year
print("Current year: ", DateCalculator.current_year()) 在上面的代码中,我们使用类方法‘current_year()’来计算当前的年份。使用类方法比多次编写相同的代码更加易于维护和编写。
4、继承和子类化
首先,什么是继承?简单来说,它是一种让一个类从另一个类上继承它的属性和方法的方式。这个被继承的类称作“父类”,而这个继承的类则称作“子类”。例如,一个动物类可以是一个父类,而狗类和猫类可以是子类。
代码示例:
class Animal: def init(self, name): self.name = name def speak(self): print("我是一只动物,我的名字是", self.name)
class Dog(Animal): def init(self, name): super().init(name) def bark(self): print("汪汪!")
class Cat(Animal): def init(self, name): super().init(name)
def meow(self): print("喵喵!")
在这个例子中,我们定义了一个 Animal 类作为一个父类,其包含一个 speak 方法,用于输出“我是一只动物,我的名字是XXX”。
接下来,我们定义了一个 Dog 类和一个 Cat 类作为 Animal 的子类。在这两个子类中,我们通过调用 super().init(name) 方法让 Dog 和 Cat 的 name 属性继承自 Animal 的 name 属性。同时,我们也为它们分别添加了 bark 和 meow 方法,用于输出不同的声音。
dog = Dog("旺财") dog.speak() dog.bark()
Output:
我是一只动物,我的名字是 旺财
汪汪!
cat = Cat("咪咪") cat.speak() cat.meow()
Output:
我是一只动物,我的名字是 咪咪
喵喵!
可以看到,我们成功地通过继承让 Dog 和 Cat 类获得了 Animal 类的 speak 方法,并且通过子类化添加了不同的方法来实现功能的扩展。这样的方式既方便又灵活,可以大大简化我们的代码。
当然,这样的继承也可以产生一些问题,比如说实例化的对象可能会因为多层继承而产生命名冲突的问题。但是,在合理使用的情况下,它是一种非常有用的编程方式。
5、多重继承
多重继承是面向对象中非常重要且使用广泛的一种继承方式。它允许我们从多个父类中继承属性和方法,从而实现更加灵活的代码设计。但是,多重继承也可能会导致一些问题,例如方法名冲突、类的复杂性增加等等。因此,在使用多重继承时需要谨慎设计,尽量避免出现问题。
假设我们有3个类,分别是Animal(动物)、Bird(鸟)和Fish(鱼)。Animal类是基类,它包含了所有动物都具有的属性和方法,例如eat、sleep等等。Bird和Fish类都是继承自Animal类的子类,并且它们分别添加了自己独有的属性和方法,例如Bird类具有fly方法,而Fish类具有swim方法。
那么,如果我们需要创建一个Penguin(企鹅)类,它既需要继承Bird类的fly方法,又需要继承Fish类的swim方法,该怎么办呢?这时候,就需要使用多重继承了。
代码示例:
class Animal: def init(self, name): self.name = name def eat(self): print(self.name + "正在吃东西...")
def sleep(self): print(self.name + "正在睡觉...")
class Bird(Animal): def fly(self): print(self.name + "正在飞翔...") class Fish(Animal): def swim(self): print(self.name + "正在游泳...") class Penguin(Bird, Fish): def init(self, name): super().init(name) def run(self): print(self.name + "正在奔跑...") 在上面的代码中,我们定义了Animal、Bird和Fish三个类,它们分别继承自Animal类,并且添加了独有的属性和方法。接着,我们定义了Penguin类,它同时继承自Bird类和Fish类,并且添加了自己的run方法。这样,Penguin类就可以同时拥有Bird类的fly方法和Fish类的swim方法了。
接下来,我们可以创建一个Penguin对象,来测试它的方法是否正常:
penguin = Penguin("小企鹅") penguin.fly() # 输出:小企鹅正在飞翔... penguin.swim() # 输出:小企鹅正在游泳... penguin.eat() # 输出:小企鹅正在吃东西... penguin.sleep() # 输出:小企鹅正在睡觉... penguin.run() # 输出:小企鹅正在奔跑... 在上面的代码中,我们创建了一个名为小企鹅的Penguin对象,并且测试了它的飞翔、游泳、吃东西、睡觉和奔跑等方法。可以看到,这些方法都能够正常运行,并且Penguin类通过多重继承成功地继承了Bird类和Fish类的属性和方法。