C# Java:只能面向对象编程
Ruby Python :面向对象编程+函数编程
在Python中一切皆对象
反射:以字符串的形式去对象中操作成员
tar_module = __import__( 'module_name' ) # 以字符串的形式导入某个模块
tar_module = __import__( ' lib.test.module_name ' , fromlist=True ) # 对于存在多层目录的模块导入方法
getattr( object,name ) 获取某个对象中的某个成员,例如获取test.py中的f1方法,tar_func = getattr( test , 'f1' )
hasattr( object,name ) 判断某个对象中是否存在某个成员,如果存在返回True,如果不存在返回False
delattr( object,name) 删除某个对象的某个成员
setattr( object,name) 设置某个对象中的某个成员,不存在该成员则创建,存在则覆盖
成员代指的属性或方法
注:上述所有操作均只生效于内存中,并不会对文件产生影响。例如你import了某个module,你使用delattr方法删除了其中某个方法,在本次程序运行期间,删除是有效的,但并不会真正删除该模块中的方法
基于反射实现Web框架的路由系统:用户访问某个URL-->后台接收该URL并判断调用哪个函数来处理该请求-->返回请求的页面
from temp import view
ref_dict = {'jack': 'f1', 'rose': 'f2', 'index': 'f3'}
url = input('请输入一个URL')
try:
func = getattr(view, ref_dict[url])
func()
except KeyError:
print('wrong url')
def f1():
print('this is jack')
def f2():
print('this is rose')
def f3():
print('this is index')
实例化一个对象后,其中存在一个 类对象指针 指向创建其的类,当对象调用方法时,自动去指针指向的类寻找方法
创建类和对象:
面向对象编程是一种编程方式,此编程方式的落地需要使用 “类” 和 “对象” 来实现,所以,面向对象编程其实就是对 “类” 和 “对象” 的使用。
类就是一个模板,模板里可以包含多个函数,函数里实现一些功能
对象则是根据模板创建的实例,通过实例对象可以执行类中的函数
# 定义一个类
class Man:
# 定义一个类的方法,传入的self为实例化的对象,通过对象来调用方法
def eat(self):
print('eat something')
jack = Man() # 类的实例,一个对象
诶,你在这里是不是有疑问了?使用函数式编程和面向对象编程方式来执行一个“方法”时函数要比面向对象简便
- 面向对象:【创建对象】【通过对象执行方法】
- 函数编程:【执行函数】
观察上述对比答案则是肯定的,然后并非绝对,场景的不同适合其的编程方式也不同。
总结:函数式的应用场景 --> 各个函数之间是独立且无共用的数据
面向对象的三大特性:
面向对象的三大特性是指:封装、继承和多态。
一、封装
封装,顾名思义就是将内容封装到某个地方,以后再去调用被封装在某处的内容。
所以,在使用面向对象的封装特性时,需要:
- 将内容封装到某处
- 从某处调用被封装的内容
第一步:将内容封装到某处
class Man:
# 构造函数,当实例化一个对象时自动调用该方法
def __init__(self, name, sex):
self.name = name
self.sex = sex
def marrid(self):
self.wife = 'rose'
# 将'jack' 'man' 分别封装到jack对象的name属性和sex属性
# 自动执行Man类__init__方法
jack = Man('jack', 'man')
# 获取jack的name属性
print(jack.name)
# 通过对象调用类的方法
jack.marrid()
print(jack.wife)
二、继承
# 父类,Animal
class Animal:
# 构造方法,设置动物的名字和种类
def __init__(self, ani_type, name):
self.name = name
self.ani_type = ani_type
def eat(self):
print('{}eat something'.format(self.name))
def walk(self):
print('{}walk away'.format(self.name))
def drink(self):
print('{}drink water'.format(self.name))
# 子类
class cat(Animal):
pass
# 实例化一个cat对象,调用父类的构造方法
mimi = cat('cat', 'mimi')
# 调用父类的eat方法
mimi.eat()
所以,对于面向对象的继承来说,其实就是将多个类共有的方法提取到父类中,子类仅需继承父类而不必一一实现每个方法。
注:除了子类和父类的称谓,你可能看到过 派生类 和 基类 ,他们与子类和父类只是叫法不同而已。
还支持这样的操作:
class Animal():
def eat(self):
print(self.name+'eat')
class Dog(Animal):
def __init__(self, name):
self.name = name
def drink(self):
print('dog\'s drking')
hu = Dog('胖虎')
hu.eat()
hu.drink()
在父类Animal中并没有定义name这个字段,但是父类的方法中用到了这个字段。这时如果实例化子类并且定义了该字段的话,可以调用父类的方法,不会报错
那么问题又来了,多继承呢?
- 是否可以继承多个类
- 如果继承的多个类每个类中都定了相同的函数,那么那一个会被使用呢?
1、Python的类可以继承多个类,Java和C#中则只能继承一个类
2、python3的类都是新式类,即所有类的父类为object
对于多继承,查找方式如下:
1、多重继承,但没有共同的父类
class Son(A, D): # 优先查找Son自己的方法,然后找A及A的父类,再找D及D的父类
pass
class A(B):
pass
class B(C):
pass
class D(E):
pass
class E(F):
pass
子类继承A,A继承B,B继承C。子类还继承D,D继承E,E继承F。那么当需要调用一个方法时,优先从子类自身去找这个方法,如果不存在,查找顺序如下:
图 1
2.多重继承,有共同的父类:
如果C和F共同继承于G,那么查找顺序依然如上图所示,最后去G中寻找
注意:在上述查找过程中,一旦找到,则寻找过程立即中断,便不会再继续找了
3.特殊情况:
图 2
代码:
class C:
def do_something(self):
print('这是C中的方法')
pass
class B(C):
def do_something(self):
print('这是B中的方法')
pass
class E:
def do_something(self):
print('这是E中的方法')
pass
class D(E):
def func(self):
self.do_something()
def do_something(self):
print('这是D中的方法')
pass
class A(B, D):
def do_something(self):
print('这是A中的方法')
pass
a = A()
a.func()
A继承于B,D。D中有一个方法func(),调用了self.do_something方法,如果A中存在do_something方法,自然会调用A中的该方法,
那么如果A中不存在该方法呢?会调用哪个父类的do_something方法呢?这时我们需要注意的是,我们实例化的是一个A类的对象
a,传递进func的self指向的是a对象,那么调用self.do_something方法相当于是调用a对象的该方法。此时如果该方法不存在,应该
遵循 图 1 中的调用顺序。
深度优先遍历
三、多态
Pyhon不支持Java和C#这一类强类型语言中多态的写法,但是原生多态,其Python崇尚“鸭子类型”。
class F1:
pass
class S1(F1):
def show(self):
print 'S1.show'
class S2(F1):
def show(self):
print 'S2.show'
# 由于在Java或C#中定义函数参数时,必须指定参数的类型
# 为了让Func函数既可以执行S1对象的show方法,又可以执行S2对象的show方法,所以,定义了一个S1和S2类的父类
# 而实际传入的参数是:S1对象和S2对象
def Func(F1 obj):
"""Func函数需要接收一个F1类型或者F1子类的类型"""
print obj.show()
s1_obj = S1()
Func(s1_obj) # 在Func函数中传入S1类的对象 s1_obj,执行 S1 的show方法,结果:S1.show
s2_obj = S2()
Func(s2_obj) # 在Func函数中传入Ss类的对象 ss_obj,执行 Ss 的show方法,结果:S2.show
总结:
以上就是本节对于面向对象初级知识的介绍,总结如下:
- 面向对象是一种编程方式,此编程方式的实现是基于对 类 和 对象 的使用
- 类 是一个模板,模板中包装了多个“函数”供使用
- 对象,根据模板创建的实例(即:对象),实例用于调用被包装在类中的函数
- 面向对象三大特性:封装、继承和多态
问题一:什么样的代码才是面向对象?
答:从简单来说,如果程序中的所有功能都是用 类 和 对象 来实现,那么就是面向对象编程了。
问题二:函数式编程 和 面向对象 如何选择?分别在什么情况下使用?
答:须知:对于 C# 和 Java 程序员来说不存在这个问题,因为该两门语言只支持面向对象编程(不支持函数式编程)。而对于 Python 和 PHP 等语言却同时支持两种编程方式,且函数式编程能完成的操作,面向对象都可以实现;而面向对象的能完成的操作,函数式编程不行(函数式编程无法实现面向对象的封装功能)。
所以,一般在Python开发中,全部使用面向对象 或面向对象和函数式混合使用
面向对象的应用场景:
1.多函数需使用共同的值,如:数据库的增、删、改、查操作都需要连接数据库字符串、主机名、用户名和密码
class SqlHelper:
def __init__(self, host, user, pwd):
self.host = host
self.user = user
self.pwd = pwd
def 增(self):
# 使用主机名、用户名、密码(self.host 、self.user 、self.pwd)打开数据库连接
# do something
# 关闭数据库连接
def 删(self):
# 使用主机名、用户名、密码(self.host 、self.user 、self.pwd)打开数据库连接
# do something
# 关闭数据库连接
def 改(self):
# 使用主机名、用户名、密码(self.host 、self.user 、self.pwd)打开数据库连接
# do something
# 关闭数据库连接
def 查(self):
# 使用主机名、用户名、密码(self.host 、self.user 、self.pwd)打开数据库连接
# do something
# 关闭数据库连接# do something
2.需要创建多个事物,每个事物属性个数相同,但是值的需求
如:张三、李四、杨五,他们都有姓名、年龄、血型,但其都是不相同。即:属性个数相同,但值不相同
class Person:
def __init__(self, name ,age ,blood_type):
self.name = name
self.age = age
self.blood_type = blood_type
def detail(self):
temp = "i am %s, age %s , blood type %s " % (self.name, self.age, self.blood_type)
print temp
zhangsan = Person('张三', 18, 'A')
lisi = Person('李四', 73, 'AB')
yangwu = Person('杨五', 84, 'A')
问题三:类和对象在内存中是如何保存?
答:类以及类中的方法在内存中只有一份,而根据类创建的每一个对象都在内存中需要存一份,大致如下图:
如上图所示,根据类创建对象时,对象中除了封装 name 和 age 的值之外,还会保存一个类对象指针,该值指向当前对象的类。
当通过 obj1 执行 【方法一】 时,过程如下:
- 根据当前对象中的 类对象指针 找到类中的方法
- 将对象 obj1 当作参数传给 方法的第一个参数 self