Python面试题之Python中的类和实例
0x00 前言
类,在学习面向对象我们可以把类当成一种规范,这个思想就我个人的体会,感觉很重要,除了封装的功能外,类作为一种规范,我们自己可以定制的规范,从这个角度来看,在以后我们学习设计模式的时候,对设计模式的理解会很有帮助。其次,语言中类是抽象的模板,用来描述具有相同属性和方法的对象的集合,比如Animal类。而实例是根据类创建出来的一个个具体的“对象”,每个对象都拥有相同的方法,但各自的数据可能不同。
Python使用class关键字来定义类,其基本结构如下:
class 类名(父类列表):
pass
类名通常采用驼峰式命名方式,尽量让字面意思体现出类的作用。Python采用多继承机制,一个类可以同时继承多个父类(也叫基类、超类),继承的基类有先后顺序,写在类名后的圆括号里。继承的父类列表可以为空,此时的圆括号可以省略。但在Python3中,即使你采用类似classStudent:pass的方法没有显式继承任何父类的定义了一个类,它也默认继承object类。因为,object是Python3中所有类的基类。
下面是一个学生类:
class Student:
classroom = ''
address = 'beijing' def __init__(self, name, age):
self.name = name
self.age = age def print_age(self):
print('%s: %s' % (self.name, self.age))
可以通过调用类的实例化方法(有的语言中也叫初始化方法或构造函数)来创建一个类的实例。默认情况下,使用类似obj=Student()的方式就可以生成一个类的实例。但是,通常每个类的实例都会有自己的实例变量,例如这里的name和age,为了在实例化的时候体现实例的不同,Python提供了一个def__init__(self):的实例化机制。任何一个类中,名字为__init__的方法就是类的实例化方法,具有__init__方法的类在实例化的时候,会自动调用该方法,并传递对应的参数。比如:
class Student:
li = Student("李四", 24)
zhang = Student("张三", 23)
0x01 实例变量和类变量
实例变量:
实例变量指的是实例本身拥有的变量。每个实例的变量在内存中都不一样。Student类中__init__方法里的name和age就是两个实例变量。通过实例名加圆点的方式调用实例变量。
我们打印下面四个变量,可以看到每个实例的变量名虽然一样,但他们保存的值却是各自独立的:
print(li.name)
print(li.age)
print(zhang.name)
print(zhang.age)
------------------------
李四
24
张三
23
类变量:
定义在类中,方法之外的变量,称作类变量。类变量是所有实例公有的变量,每一个实例都可以访问、修改类变量。在Student类中,classroom和address两个变量就是类变量。可以通过类名或者实例名加圆点的方式访问类变量,比如:
Student.classroom
Student.address
li.classroom
zhang.address
在使用实例变量和类变量的时候一定要注意,使用类似zhang.name访问变量的时候,实例会先在自己的实例变量列表里查找是否有这个实例变量,如果没有,那么它就会去类变量列表里找,如果还没有,弹出异常。
Python动态语言的特点,让我们可以随时给实例添加新的实例变量,给类添加新的类变量和方法。因此,在使用li.classroom = '102'的时候,要么是给已有的实例变量classroom重新赋值,要么就是新建一个li专属的实例变量classroom并赋值为‘102’。看下面的例子
>>> class Student: # 类的定义体
classroom = '' # 类变量
address = 'beijing' def __init__(self, name, age):
self.name = name
self.age = age def print_age(self):
print('%s: %s' % (self.name, self.age)) >>> li = Student("李四", 24) # 创建一个实例
>>> zhang = Student("张三", 23) # 创建第二个实例
>>> li.classroom # li本身没有classroom实例变量,所以去寻找类变量,它找到了!
''
>>> zhang.classroom # 与li同理
''
>>> Student.classroom # 通过类名访问类变量
''
>>> li.classroom = '' # 关键的一步!实际是为li创建了独有的实例变量,只不过名字和类变量一样,都叫做classroom。
>>> li.classroom # 再次访问的时候,访问到的是li自己的实例变量classroom
''
>>> zhang.classroom # zhang没有实例变量classroom,依然访问类变量classroom
''
>>> Student.classroom # 保持不变
''
>>> del li.classroom # 删除了li的实例变量classroom
>>> li.classroom # 一切恢复了原样
''
>>> zhang.classroom
''
>>> Student.classroom
''
0x03 类的方法
Python的类中包含实例方法、静态方法和类方法三种方法。这些方法无论是在代码编排中还是内存中都归属于类,区别在于传入的参数和调用方式不同。在类的内部,使用def关键字来定义一个方法。
实例方法
类的实例方法由实例调用,至少包含一个self参数,且为第一个参数。执行实例方法时,会自动将调用该方法的实例赋值给self。self代表的是类的实例,而非类本身。self不是关键字,而是Python约定成俗的命名,你完全可以取别的名字,但不建议这么做。
例如,我们前面Student类中的print_age()就是实例方法:
def print_age(self):
print('%s: %s' % (self.name, self.age)) # --------------------------
# 调用方法
li.print_age()
zhang.print_age()
静态方法
静态方法由类调用,无默认参数。将实例方法参数中的self去掉,然后在方法定义上方加上@staticmethod,就成为静态方法。它属于类,和实例无关。建议只使用类名.静态方法的调用方式。(虽然也可以使用实例名.静态方法的方式调用)
class Foo: @staticmethod
def static_method():
pass #调用方法
Foo.static_method()
类方法
类方法由类调用,采用@classmethod装饰,至少传入一个cls(代指类本身,类似self)参数。执行类方法时,自动将调用该方法的类赋值给cls。建议只使用类名.类方法的调用方式。(虽然也可以使用实例名.类方法的方式调用)
class Foo: @classmethod
def class_method(cls):
pass Foo.class_method()
看一个综合例子:
class Foo: def __init__(self, name):
self.name = name def ord_func(self):
"""定义实例方法,至少有一个self参数 """
print('实例方法') @classmethod
def class_func(cls):
""" 定义类方法,至少有一个cls参数 """
print('类方法') @staticmethod
def static_func():
""" 定义静态方法 ,无默认参数"""
print('静态方法')
# 调用实例方法
f = Foo("Jack")
f.ord_func()
Foo.ord_func(f) # 请注意这种调用方式,虽然可行,但建议不要这么做! # 调用类方法
Foo.class_func()
f.class_func() # 请注意这种调用方式,虽然可行,但建议不要这么做! # 调用静态方法
Foo.static_func()
f.static_func() # 请注意这种调用方式,虽然可行,但建议不要这么做!
0x04 类、类的方法、类变量、类的实例和实例变量在内存中是如何保存的
类、类的所有方法以及类变量在内存中只有一份,所有的实例共享它们。而每一个实例都在内存中独立的保存自己和自己的实例变量。
创建实例时,实例中除了封装诸如name和age的实例变量之外,还会保存一个类对象指针,该值指向实例所属的类的地址。因此,实例可以寻找到自己的类,并进行相关调用,而类无法寻找到自己的某个实例。
0x05 Python 类的继承
在ptyhon中类一个类是可以同时继承多个类,语法:
class 类名(父类1,父类2,...) 类体
Python类继承之深度优先
python 支持多继承,但对与经典类和新式类来说,多继承查找的顺序是不一样的。
经典类:
class P1:
def foo(self):
print 'p1-foo' class P2 :
def foo(self):
print 'p2-foo' def bar(self):
print 'p2-bar' class C1 (P1,P2):
pass class C2 (P1,P2):
def bar(self):
print 'C2-bar' class D(C1,C2):
pass
实例d调用foo()时,搜索顺序是 D => C1 => P1
实例d调用bar()时,搜索顺序是 D => C1 => P1 => P2
换句话说,经典类的搜索方式是按照“从左至右,深度优先”的方式去查找属性。d先查找自身是否有foo方法,没有则查找最近的父类C1里是否有该方法,如果没有则继续向上查找,直到在P1中找到该方法,查找结束。
Python类继承之广度优先
新式类:
class P1(object):
def foo(self):
print 'p1-foo' class P2(object):
def foo(self):
print 'p2-foo' def bar(self):
print 'p2-bar' class C1 (P1,P2):
pass class C2 (P1,P2):
def bar(self):
print 'C2-bar' class D(C1,C2):
pass
实例d调用foo()时,搜索顺序是 D => C1 => C2 => P1
实例d调用bar()时,搜索顺序是 D => C1 => C2
可以看出,新式类的搜索方式是采用“广度优先”的方式去查找属性。
参考
Python面试题之Python中的类和实例的更多相关文章
- python中的类和实例
今天花了两个多小时后搜索相关博客看了看python中有关类和实例的介绍,差不多大概明白了. python中的类和c++中的类是一样的,不同之处就是c++的类,如果含有成员变量,并且成员变量发生变化后, ...
- 千万不要错过这几道Python面试题,Python面试题No16
第1题: python下多线程的限制以及多进程中传递参数的方式? python多线程有个全局解释器锁(global interpreter lock),简称GIL,这个GIL并不是python的特性, ...
- Android内核sysfs中switch类使用实例
Android内核sysfs中switch类使用实例 最终在这个周末,能够干点自己想要干的事了. 由我这个二流的内核驱动开发人员来解析一下sysfs中的switch类.先猜測一下来历,在普通的嵌入式L ...
- java_字段初始化的规律、静态方法中访问类的实例成员、查询创建对象的个数
字段初始化规律: 当执行如下代码时 class InitializeBlockClass{ public int field=100; { field=200; } public Initialize ...
- 在java的静态方法中访问类的实例成员
直接来看代码: public class Example { int x = 3;//类的实例变量,初始化值为3 static int y = 4;//类的静态变量,初始化值为4 public sta ...
- Python面试题整理-更新中
几个链接: 编程零基础应当如何开始学习 Python ? - 路人甲的回答 网易云课堂上有哪些值得推荐的 Python 教程? - 路人甲的回答 怎么用最短时间高效而踏实地学习 Python? - 路 ...
- Python面试题之Python中type和object的关系
知乎上看到的提问: 两个是互为实例的关系,但不是互为子类的关系,只有type是object的子类,反之则不成立. 大牛说两者是蛋生鸡鸡生蛋的关系,但我还是不明白,有懂的麻烦解释一下, 希望不要给出外文 ...
- Python面试题之Python中的lambda map filter reduce zip
当年龟叔想把上面列出来的这些都干掉.在 “All Things Pythonic: The fate of reduce() in Python 3000”这篇文章中,他给出了自己要移除lambda. ...
- Python面试题之Python面试题汇总
在这篇文章中: Python基础篇 1:为什么学习Python 2:通过什么途径学习Python 3:谈谈对Python和其他语言的区别 Python的优势: 4:简述解释型和编译型编程语言 5:Py ...
随机推荐
- 《C++ Primer Plus》15.1 友元 学习笔记
15.1.1 友元类假定需要编写一个模拟电视机和遥控器的简单程序.决定定义一个Tv类和一个Remote类,来分别表示电视机和遥控器.遥控器和电视机之间既不是is-a关系也不是has-a关系.事实上,遥 ...
- iOS-利用插件实时刷新模拟器(提高效率)
解决办法: 1.需要给Xcode安装一个Alcatraz插件 安装完成后:点击window 下面的 package manager 安装我们今天的主角 2. ‘Injection Plugin for ...
- click() bind() live() delegate()区别
click(),bind(),live()都是执行事件时使用的方法 1.click()单击事件方法: $("a").click(function() { alert("h ...
- EUI组件之DataGroup
看官网教程,这个没法单独用. http://developer.egret.com/cn/github/egret-docs/extension/EUI/dataCollection/dataGrou ...
- 【BZOJ4337】BJOI2015 树的同构 括号序列
[BZOJ4337]BJOI2015 树的同构 Description 树是一种很常见的数据结构. 我们把N个点,N-1条边的连通无向图称为树. 若将某个点作为根,从根开始遍历,则其它的点都有一个前驱 ...
- [SQL] SQL 修复命令
You should run the repair from the original installation media, using the following command line ...
- 【Android】Android 发送短信和打电话的方法
发送短信的方法 有两种方法可以实现发送短信,其一是使用intent-startActivity,URI数据格式为"smsto:num",调用的action为Intent.ACTIO ...
- java jar命令及补丁方法
用法: jar {ctxui}[vfmn0PMe] [jar-file] [manifest-file] [entry-point] [-C dir] files ...选项: -c 创建新档案 -t ...
- JavaScript获取地址栏的参数!
第一种方式:手动解析 “location”对象 console.log(window.location); 使用这个对象做跳转: window.location.href=""; ...
- 【巷子】:关于Apply、call、bind的详解
call方法: 语法:call(thisObj,'',''........) 定义:调用一个对象的一个方法,以另一个对象替换当前对象 说明:call方法可以用来代替另一个对象调用一个方法.call方法 ...