Python是一门面向对象(object)的语言,一切皆对象!
程序运行当中,所有的数据都是存储到内存当中然后再运行的!
对象就是内存中专门用来存储指定数据的一块区域
对象实际上就是一个容器,专门用来存储各种数据(比如:数字、布尔值、代码)
对象由三部分组成:
对象与变量
常用的对象都是Python内置的对象
# int() float() bool() str() list() dict() .... 这些都是类
a = int(10) # 创建一个int类的实例 等价于 a = 10
print(type(a)) #
但是内置对象并不能满足所有的需求,经常需要自定义一些对象**(根据类创建对象)**
类的定义方式,使用class关键字
在定义类时,可以定义变量(属性)和函数(方法)
类属性:在类中定义的变量,是公共属性,
实例属性:通过实例对象添加的属性属于实例属性(在init()方法中定义)
方法:在类中定义的函数(方法),可以通过该类的所有实例来访问(对象.方法名())
实例方法:在类中定义,以self为第一个参数的方法都是实例方法
类方法:在类内部使用 @classmethod 修饰的方法(装饰器,详见3.1.2)
静态方法:在类中使用 @staticmethod 修饰的方法
方法示例见2.3.2
类中方法的self形参说明:
在方法中不能直接访问类中的属性(包括公共属性)
# 我们自定义的类都需要使用大写字母开头,使用大驼峰命名法(帕斯卡命名法)来对类命名
class 类名(父类) : # 默认继承object类,可不写
# 公共属性
public_attribute = ''
# 对象的初始化方法(非必要,通常会有该方法)
def __init__(self,...):
pass
# 其他的方法
def method_1(self,...):
pass
def method_2(self,...):
pass
"""
练习:尝试自定义一个表示狗的类(Dog)
属性:
name
age
方法:
jiao()
run()
"""
class Dog(object) :
# 主人姓名
owner = 'qy'
# 对象的初始化方法
def __init__(self, name, age):
self.name = name
self.age = age
# 其他的方法
def jiao(self):
print("汪汪汪")
def run(self):
print("跑")
# 在方法中访问类的属性
def say_hello(self):
print("你好,%s" % self.owner)
使用类创建对象与调用函数的方式类似,对象名 = 类名(“用于初始化的参数”)
dog1 = Dog("xiaobai", 3)
print(dog1.owner) # qy
print(dog1.name) # xiaobai
dog1.name = "xiaohei"
print(dog1.name) # xiaohei
dog1.jiao() # 汪汪汪
dog1.run() # 跑
dog1.say_hello() # 你好,qy
dog2 = Dog("dahuang", 2)的运行流程
dog2.jiao() # 汪汪汪
# 方法每次被调用时,解析器都会自动传递第一个实参(调用方法的对象本身)给self,
# 如果是dog1调的,则第一个参数就是dog1对象
# 如果是dog2调的,则第一个参数就是dog2对象
使用isinstance()检查一个对象是否是一个类的实例
flag = isinstance(dog1, dog) # true
flag = isinstance(dog1, int) # false
没有init(self)函数时,可以直接设置并修改对象的属性(不建议)
# 也就是对象中实际上什么都没有,就相当于是一个空的盒子
# 可以向对象中添加变量,对象中的变量称为属性(对象.属性名 = 属性值)
class MyClass():
pass
mc1 = MyClass()
result = isinstance(mc1, MyClass) # true
print(id(mc1), type(mc1)) # 2841238613072
print(id(MyClass), type(MyClass)) # 2841239528544
mc1.name = '孙悟空'
print(mc1.name) # 孙悟空
mc1.name = '猪八戒'
print(mc1.name) # 猪八戒
# 手动给类的对象添加、修改属性容易出现错误,且不够清晰,不容易记忆
# 如果明确类的对象必须有nam,则可以将该属性设置到初始化函数中,即init(self, name),在创建对象时,必须设置name属性,如果不设置对象将无法创建
# class MyClass():
# def __init__(self, name):
# # 通过self向新建的对象中初始化属性
# self.name = name
# mc2 = MyClass("唐僧")
类对象和实例对象中都可以保存属性(方法)
如果这个属性(方法)是所有的实例共享的,则应该将其保存到类对象中
如果这个属性(方法)是某个实例独有,则应该保存到实例对象中
一般情况下,属性保存到实例对象中,而方法需要保存到类对象中
属性和方法查找的流程
在类中可以定义一些特殊方法(魔术方法)
其他特殊方法
# 定义一个Person类,默认继承Object类
class Person():
"""人类"""
def __init__(self, name , age):
self.name = name
self.age = age
# object.__str__(self)
# 会在尝试将对象转换为字符串的时候调用
# 它的作用可以用来指定对象转换为字符串的结果(print函数)
# 相当于方法重写
def __str__(self):
return 'Person [name=%s , age=%d]'%(self.name,self.age)
# object.__bool__(self)
# 可以通过bool来指定对象转换为布尔值的情况
def __bool__(self):
return self.age > 17
# object.__gt__(self, other) 大于
# 需要两个参数,一个self表示当前对象,other表示和当前对象比较的对象
def __gt__(self , other):
return self.age > other.age
# 创建两个Person类的实例
p1 = Person('孙悟空',18)
p2 = Person('猪八戒',28)
# 打印p1
# 当我们打印一个对象时,实际上打印的是对象的中特殊方法 __str__()的返回值
print(p1) # Person [name=孙悟空 , age=18]
print(bool(p1)) # True
if p1:
print(p1.name,'已经成年了')
else:
print(p1.name,'还未成年了')
print(p1 > p2) # False
print(p1 < p2) # True
# 定义一个类
class A(object):
"""
类属性
实例属性
类方法
实例方法
静态方法
"""
# 类属性
count = 0
def __init__(self):
# 实例属性
self.name = '孙悟空'
# 实例方法
# 在类中定义,以self为第一个参数的方法都是实例方法
def test(self):
print('这是test方法~~~' , self)
# 类方法
# 在类内部使用 @classmethod 来修饰的方法属于类方法
@classmethod
def test_2(cls):
print('这是test_2方法,他是一个类方法~~~ ',cls)
print(cls.count)
# 静态方法
# 在类中使用 @staticmethod 来修饰的方法属于静态方法
@staticmethod
def test_3():
print('test_3执行了~~~')
a = A()
a.test() # 这是test方法~~~ <__main__.A object at 0x000002870FBCC670>
# 通过类调用实例方法,必须手动传递self
A.test(a) # 这是test方法~~~ <__main__.A object at 0x000002870FBCC670>
A.test_2() # 等价于 a.test_2()
# 这是test_2方法,他是一个类方法~~~
# 0
A.test_3() # 等价于a.test_3()
# test_3执行了~~~
面向对象的三大特征:
面向对象编程,即定义类(创建对象),以类的变量(属性)和函数(方法)为核心
封装是面向对象的三大特性之一
可选方式一:将属性名设置为外部未知,并提供getter和setter方法使外部可以访问、设置属性值
class Dog:
def __init__(self , name , age):
self.hidden_name = name
def get_name(self):
return self.hidden_name
def set_name(self , name):
self.hidden_name = name
d = Dog('旺财')
# 调用setter来修改name属性
d.set_name('小黑')
print(d.get_name()) # 小黑
使用封装,增加了类的复杂度,但是也确保了数据的安全
可选方式二:使用双下划线开头,__属性名
class Dog:
def __init__(self , name):
self.__name = name
dog1 = Dog("xiaobai")
# print(dog1.__name) # 'Dog' object has no attribute '__name'
print(dog1._Dog__name) # xiaobai
可选方式三:使用单下划线开头,_属性名
class Person:
def __init__(self,name):
self._name = name
def get_name(self):
return self._name
def set_name(self , name):
self._name = name
p = Person('孙悟空')
print(p._name) # 孙悟空
p._name = '猪八戒'
print(p._name) # 猪八戒
装饰器就是函数闭包的一种应用
函数闭包相关内容:http://t.csdn.cn/w1lHd
常用装饰器
class Person:
def __init__(self,name,age):
self._name = name
self._age = age
# property装饰器,用来将一个get方法,转换为对象的属性
# 添加为property装饰器以后,我们就可以像调用属性一样使用get方法
@property
def name(self):
print('get方法执行了~~~')
return self._name
# setter方法的装饰器:@属性名.setter
@name.setter
def name(self , name):
print('setter方法调用了')
self._name = name
@property
def age(self):
return self._age
@age.setter
def age(self , age):
self._age = age
p = Person('猪八戒',18)
"""
setter方法调用了
get方法执行了~~~
"""
p.name = '孙悟空'
p.age = 28
print(p.name,p.age) # 孙悟空 28
继承是面向对象三大特性之一
通过继承可以使一个类(子类)获取到其他类(父类)中的属性和方法,避免编写重复性的代码,并且也符合OCP原则
在定义类时,可以在类名后的括号中指定当前类的父类(超类、基类、super)
issubclass(A, B) 检查一个类(A)是否是另一个类(B)的子类
类似的,isinstance(A, B)用来检查一个对象(A)是否是一个类(B)的实例
# 举例:有一个Animal类,包含两个方法run()、sleep(),能够实现大部分功能,但是不能实现全部功能
# 如何能让这个类来实现全部的功能呢?
# 1、直接修改这个类,在这个类中添加我们需要的功能
# - 修改起来会比较麻烦,并且会违反OCP原则
# 2、直接创建一个新的类
# - 比较麻烦,会出现大量的重复性代码
# 3、直接从Animal类中来继承它的属性和方法
# 定义类 Animal(动物)
# 这个类中有两个方法:run()、sleep()
class Animal:
def run(self):
print('动物会跑~~~')
def sleep(self):
print('动物睡觉~~~')
class Dog(Animal):
def bark(self):
print('汪汪汪~~~')
def run(self):
print('狗跑~~~~')
class Hashiqi(Dog):
def fan_sha(self):
print('我是一只傻傻的哈士奇')
d = Dog()
h = Hashiqi()
d.run() # 狗跑~~~~
d.sleep() # 动物睡觉~~~
d.bark() # 汪汪汪~~~
# issubclass() 检查一个类是否是另一个类的子类
print(issubclass(Animal , Dog)) # False
print(issubclass(Animal , object)) # True
print(issubclass(Person , object)) # True
普通方法重写
如果子类中有和父类同名的方法,则通过子类实例调用方法时,会调用子类的方法而不是父类的方法,叫做方法的重写(覆盖,override)
class Animal:
def run(self):
print('动物会跑~~~')
def sleep(self):
print('动物睡觉~~~')
class Dog(Animal):
def bark(self):
print('汪汪汪~~~')
def run(self):
print('狗跑~~~~')
# 创建Dog类的实例
d = Dog()
d.run() # 狗跑~~~~
class A(object):
def test(self):
print('AAA')
class B(A):
def test(self):
print('BBB')
class C(B):
def test(self):
print('CCC')
# 创建一个c的实例
c = C()
c.test() # CCC
特殊方法重写
class Animal:
def __init__(self,name):
self._name = name
def run(self):
print('动物会跑~~~')
@property
def name(self):
return self._name
@name.setter
def name(self,name):
self._name = name
# 父类中的所有方法都会被子类继承,包括特殊方法,也可以重写特殊方法
class Dog(Animal):
def __init__(self,name,age):
# 在子类的初始化函数中初始化父类的属性
# super() 可以用来获取当前类的父类,通过super()返回对象调用父类方法
super().__init__(name)
# 子类属性初始化
self._age = age
def run(self):
print('狗跑~~~~')
@property
def age(self):
return self._age
@age.setter
def age(self,age):
self._age = age
d = Dog('旺财',18)
print(d.name) # 旺财
print(d.age) # 18
python支持多重继承,即可以为一个类同时指定多个父类
class A(object):
def test(self):
print('AAA')
class B(object):
def test(self):
print('B中的test()方法~~')
def test2(self):
print('BBB')
class C(A,B):
pass
# 类名.__bases__ 这个属性可以用来获取当前类的所有父类
print(B.__bases__) # (,)
print(C.__bases__) # (, )
c = C()
c.test() # AAA
c.test2() # BBB
多态是面向对象的三大特征之一
多态从字面上理解是一个对象可以有不同的形态:例如动物可以分为狗、猫…
多态的概念依赖于继承
class Animal:
def run(self):
print('动物会跑~~~')
def sleep(self):
print('动物睡觉~~~')
class Dog(Animal):
def bark(self):
print('汪汪汪~~~')
def run(self):
print('狗跑~~~~')
class Cat(Animal):
def bark(self):
print('喵喵喵~~~')
def run(self):
print('猫跑~~~~')
a = Dog()
a.bark() # 汪汪汪~~~
b = Cat()
b.bark() # 喵喵喵~~~
多态性:向不同的对象发送同一条消息,不同的对象在接收时会产生不同的行为(即方法)
# 定义say_hello()函数,只要对象中含有bark()方法,它就可以作为参数传递
# 这个函数并不会考虑对象的类型
def say_hello(obj):
obj.bark()
a = Dog()
say_hello(a) # 汪汪汪~~~
b = Dog()
say_hello(b) # 喵喵喵~~~
# 在函数中可以对输入变量做类型检查(不常用),例如,只有obj是Dog类型的对象时,才可以正常使用,其他类型的对象都无法使用该函数
# 这个函数就不具备多态性,函数的适应性非常的差
def say_hello_2(obj):
# 做类型检查,isinstance()在很少使用
if isinstance(obj , Dog):
obj.bark()
鸭子类型:如果一个东西,走路像鸭子,叫声像鸭子,那么它就是鸭子
class Duck():
def walk(self):
print('I walk like a duck')
def swim(self):
print('i swim like a duck')
class Person():
def walk(self):
print('this one walk like a duck')
def swim(self):
print('this man swim like a duck')
# Person类拥有跟Duck类一样的方法
# 当有一个函数调用Duck类,并利用到了两个方法walk()和swim(),传入Person类也一样可以运行
# 函数并不会检查对象的类型是不是Duck,只要他拥有walk()和swim()方法,就可以正确的被调用。
再举例,如果一个对象实现了getitem方法,那python的解释器就会把它当做一个collection,就可以在这个对象上使用切片,获取子项等方法;
如果一个对象实现了iter和next方法,python就会认为它是一个iterator,就可以在这个对象上通过循环来获取各个子项。
相关链接:python中对多态的理解 - 宇文哲的文章 - 知乎 https://zhuanlan.zhihu.com/p/88402677
模块化指将一个完整的程序分解为一个一个小的模块,通过将模块组合,搭建出一个完整的程序
模块化的优点:方便开发、方便维护、模块可复用
python中一个py文件就是一个模块,要想创建模块,实际上就是创建一个python文件
在一个模块中引入外部模块
① import 模块名 (模块名,就是python文件的名字)
② import 模块名 as 模块别名
import可以在程序的任意位置调用,但是一般会统一写在程序的开头
可以引入同一个模块多次,但是模块的实例只会创建一个
在每一个模块内部都有一个name属性,通过这个属性可以获取到模块的名字
name属性值为main的模块是主模块,一个程序中只会有一个主模块
import test_module as test
print(test.__name__) # test_module
print(__name__) # __main__
也可以只引入模块中的部分内容
① from 模块名 import 变量,变量…
② from 模块名 import 变量 as 别名
引入模块的所有内容(不建议)
访问模块中的变量:模块名.变量名
包也是一个模块
python中有自动的垃圾回收机制,它会自动将这些没有被引用的对象删除,不用手动处理垃圾回收
class A:
def __init__(self):
self.name = 'A类'
# del是一个特殊方法,它会在对象被垃圾回收前调用
def __del__(self):
print('A()对象被删除了~~~',self)
a = A()
print(a.name) # A类
a = None # 将a设置为了None,此时没有任何的变量对A()对象进行引用,它就是变成了垃圾,所谓的垃圾回收就是将垃圾对象从内存中删除
del a # A()对象被删除了~~~ <__main__.A object at 0x000001BEDE75C5E0>