Python学习_11_类和实例
类和实例
类是对象创建实例的模板,而实例则是对象的实体。类使用class关键字定义:
class MyClass:
pass
python中创建实例直接使用工厂函数(类名加上一对括号),和其他的语言使用new关键字有所不同:
my_obj = MyClass()
一般来说,类名以大写字母开头,而对象名则以小写字母或者下划线开头。
实例化对象时,会执行类定义中的__init__()方法,该方法执行时包含实例的各种初始化操作。
方法和函数的区别:方法由对象调用,在方法定义中,第一个参数必须是显示的self,表示当前对象,而函数则不传入当前对象self,可以说方法是特殊的函数,他指定了调用对象,并且可以获得调用对象的属性。函数和方法都是function类的实例:
class MyClass(object):
def __init__(self):
pass
def method1(a,b):
print(a+b)
def method2(self,a,b):
print(a+b)
obj = MyClass()
MyClass.method1(1,2)
obj.method2(1,2)
print(type(MyClass.method1))
print(type(MyClass.method2))
上述代码中,obj.method2就是方法,而MyClass.method1是一个普通的函数,为什么要使用MyClass.method1是因为method1的命名空间包含在MyClass中,而使用obj.method2的原因是因为method2是obj所在类的一个方法,并obj作为method1调用的对象(method1获得obj的绑定)。
从上述代码还可以延生出以下结论:
1. MyClass.method2(1,2)将会产生错误:TypeError: method2() takes exactly 3 arguments (2 given)。 而MyClass也是一个对象,执行MyClass.method2()时,并不能将MyClass作为self代表的当前对象,这是因为实例的方法必须存在于类中,而实例的属性则存在于实例本身,MyClass调用method2不成功是因为MyClass所在的类:type并没有该方法,MyClass.method2不能获得MyClass的绑定,所以MyClass.method2()仅仅是执行MyClass命名空间中的一个函数,而该函数要求传入一个对象,所以导致了TypeError错误。需要显式地传入对象来正确调用:MyClass.method2(MyClass,1,2)
2. obj.method1(1,2)将会产生错误:TypeError: method1() takes exactly 2 positional arguments (3 given)。虽然调用报错,但是报错的原因仅仅是因为参数数量不匹配,由此可见,一个对象调用方法时,该方法必须要显式的第一个对象为self。并且,obj调用时,并没有使用MyClass命名空间,因为obj是MyClass的实例,将会集成MyClass的命名空间。
注意:__init__()方法不应该返回任何对象,而应该返回None,因为该方法本身是一个构造器,实例对象通过该构造器实例化后返回,如果在该方法中显示返回了其他对象,会造成冲突。
对象的属性
在python中,实例可以在任何时刻添加对象,即使在类中并没有定义该属性,但是该语句并不会有任何错误,并且在之后一样可以调用该属性,这样的特性可能非常的方便,但是也隐藏了很多弊端,容易对已知的对象传入不必要的属性,从而搞得一团糟:
class MyClass:
def __init__(self,a,b):
self.a = a
self.b = b
obj = MyClass(1,2)
print(obj.a)
obj.a = 3
print(obj.a)
def a_method(self,c):
self.c = c
from types import MethodType
obj.a_method = MethodType(a_method,obj)
obj.a_method(4)
print(obj.c)
上述通过类定义之外的途径和对象添加方法和属性,在其他的同类的对象中是不具有的,在ruby中通过单件类来完成,python中,方法名是一个变量,该变量对方法进行了引用,本质上都是对象,也可以看成是属性,所谓的单件方法在python中就是实例的特有属性,方法和属性都遵循python从局部命名空间到全局命名空间到内建命名空间的顺序查找。
通过__slots__可以限制类的属性,以防止对实例添加不必要的变量:
class MyClass:
__slots___ = (‘a’,’b')
...
此时,如果再给obj添加其他的属性将会告知MyClass中没有该属性的错误,从而限制了对象的属性。
对象的属性是不再能被任意添加了,但是仍然可以在外部可以被外部代码轻松访问,通过在变量前添加两个下划线来表示私有变量:
class Student:
__slots__ = ('score','grade')
def __init__(self,score):
self.score = score
def getScore(self):
if self.score >= 90:
self.grade = 'A'
elif self.score >= 60:
self.grade = 'B'
elif self.score < 60:
self.grade = 'C'
return self.grade
xiaoming = Student(47)
xiaoming.score = 90
print(xiaoming.getScore())
在上述代码中,虽然在初始化小明的成绩的时候输入了47分,但是仍然可以在外部对该成绩进行修改,由于小明懂了一点python,瞬间就从差等生变成优等生,这样的结果是不希望被看到的。将score改为__score后:
class Student:
__slots__ = ('__score','grade')
def __init__(self,score):
self.__score = score
def getGrade(self):
if self.__score >= 90:
self.grade = 'A'
elif self.__score >= 60:
self.grade = 'B'
elif self.__score < 60:
self.grade = 'C'
return self.grade
此时,小明的投机取巧修改自己分数的手段就不再生效。访问xiaoming.__score时,将出现以下错误:AttributeError: 'Student' object has no attribute '__score'
尽管如此,其实小明还是可以这样做:
xiaoming._Student__score = 100
因为在使用双下划线时,python只不过是讲变量的名字之前加入了_Classname前缀以避免被直接访问到(不同版本的解释器会有所不同),而不是真正的不可访问,python中并没有真正不可操作的机制,而类似于_Student这些单下划线开头的变量名,虽然可以被外部访问到,但是最好不要这样做,单下划线的意思就是,虽然我不是私有变量,但是请你把我当成私有变量,这或许是对python中没有私有变量的一种妥协吧。
此时,我也没有办法来对付小明了。这是很有意思的故事,在Ruby中,任何实例变量在设置set方法和get方法之前,都是不能被外部访问到的,只有通过给私有变量设置setter方法或者getter方法,该变量才真正的成为属性,总之为了实例变量成为属性要做一些必要工作。而python中,几乎不能阻止一个对象访问在类中定义的变量,甚至如果你不用__slots__,都无法阻止添加各种属性到对象中,我们要做的工作反而变成想方设法让属性不能被访问到。
使用真正的属性
python也可以和其他语言一样,通过@property修饰器,实现getter、setter方法从而访问和设置的属性,从而可以“掩耳盗铃”地说,你不能设置该属性:
class Student:
__slots__ = ('_score','_grade')
def __init__(self,score):
self._score = score
@property
def score(self):
return _score
@property
def grade(self):
if self._score >= 90:
self._grade = 'A'
elif self._score >= 60:
self._grade = 'B'
elif self._score < 60:
self._grade = 'C'
return self._grade
当使用了@property修饰器之后,其实就是实现了getter方法,使外部可以被看到,如果想要设置getter方法,则再添加:
@score.setter
def score(self,value):
score._score = value
和其他语言一样,方法名字就是属性名字,小明可以通过xiaoming.score来看到自己的成绩,当没有定义setter方法时,小明是不能通过xiaoming.score来修改自己的成绩的,但是这是不是就意味着小明真的不能改了呢?其实通过上面的代码已经可以看到,python内部的变量名字并不是score,而是_score,这就是为什么我说“掩耳盗铃”的原因,小明依然可以通过xiaoming._score来修改自己的成绩。总之,python中是不会真正阻止一个对象访问类定义中的变量的,这实在有点令人苦恼。
关于属性的补充
1.和其他动态语言一样,可以在执行过程中查看对象中的属性,通过dir()方法,可以打印一个对象的所有属性组成的列表。另外一个不幸的消息,python3中取消了对象的__dict__属性,我不知道从哪里可以找回来。
2.或许会有类属性这样的说法,其实完全可以看做是类的命名空间中的一个变量。因为在type中并没有使用__slots__,所以在类中添加的任何变量,类本身和实例都可以访问,其中,实例访问该属性的时候,其实是建立了一个该属性的引用。所以当实例尝试去改变这个值的时候,如果值是不可变类型,则此时实例其实是添加了一个同名的属性,引用的其实是其他的值,而并非类中定义的属性。到这里可以发现,如果该属性是可变类型,则当实例去改变该值的时候,并没有重新引用其他的值,而是真正的改变了该属性:
class A:
a = 1
b = {'a':'A'}
o = A()
print(o.a)
print(id(o.a))
o.a = 2
print(id(o.a))
print(id(A.a))
A.a = 3
print(id(A.a))
print(o.a)
print(o.b)
print(id(o.b))
o.b['b'] = 'B'
print(o.b)
print(id(o.b))
print(A.b)
以上代码结果为(id会有所不同):
1
4296853120
4296853152
4296853120
4296853184
2
{'a': 'A'}
4301317296
{'a': 'A', 'b': 'B'}
4301317296
{'a': 'A', 'b': 'B'}
需要注意的是,第六个print函数结果为2,而不是A.a修改过的3,如果理解了整个过程,这是显而易见的:因为现在实例引用的已经是新的变量,而不是类属性,类属性的更改已经不会对其造成影响。
Python学习_11_类和实例的更多相关文章
- Python学习:类和实例
Python学习:类和实例 本文作者: 玄魂工作室--热热的蚂蚁 类,在学习面向对象我们可以把类当成一种规范,这个思想就我个人的体会,感觉很重要,除了封装的功能外,类作为一种规范,我们自己可以定制的规 ...
- 廖雪峰Python学习笔记——类和实例
Class MyList(list): __metaclass__ = ListMetaclass #它表示在创建MyList这个类时,必须通过 ListMetaclass这个元类的LIstMetac ...
- python学习_数据处理编程实例(二)
在上一节python学习_数据处理编程实例(二)的基础上数据发生了变化,文件中除了学生的成绩外,新增了学生姓名和出生年月的信息,因此将要成变成:分别根据姓名输出每个学生的无重复的前三个最好成绩和出生年 ...
- python中的类和实例
今天花了两个多小时后搜索相关博客看了看python中有关类和实例的介绍,差不多大概明白了. python中的类和c++中的类是一样的,不同之处就是c++的类,如果含有成员变量,并且成员变量发生变化后, ...
- Python面试题之Python中的类和实例
0x00 前言 类,在学习面向对象我们可以把类当成一种规范,这个思想就我个人的体会,感觉很重要,除了封装的功能外,类作为一种规范,我们自己可以定制的规范,从这个角度来看,在以后我们学习设计模式的时候, ...
- Python学习-day7 类 部分socket
这周还是继续关于类的学习,在面向对象的学习过程中又学习了网络编程,并且提交了编写FTP的作业. 复习一下类的相关概念和定义 类 属性 实例变量:内存中 ...
- Python进阶_类与实例
上一节将到面对对象必须先抽象模型,之后直接利用模型.这一节我们来具体理解一下这句话的意思. 面对对象最重要的概念就是类(class)和实例(instance),必须牢记类是抽象的模板,比如studen ...
- python之定义类创建实例
https://www.cnblogs.com/evablogs/p/6688938.html 类的定义 在Python中,类通过class关键字定义,类名以大写字母开头 1 2 >>&g ...
- Python面向对象编程 -- 类和实例、访问限制
面向对象编程 Object Oriented Programming,简称OOP,是一种程序设计思想.OOP把对象作为程序的基本单元,一个对象包含了数据和操作数据的函数. 面向过程的程序设计把计算机程 ...
随机推荐
- UWP ListView下模板宽度问题
在ListView中模板的宽度默认不会根据listView的宽度改变为改变,这需要更改一下设置,重点在于设置HorizontalContentAlignment的拉伸模式.注意是HorizontalC ...
- AspNet Core 程序写入配置信息并再次读取配置信息
1.首先创见Core控制台应用程序 并且引入 AspNetCore.All 首先我们写入配置信息:直接代码如下 //配置信息的根对象 public static IConfigurationRoo ...
- 探索版 webstorm快捷方式
ctrl + alt + s 打开配置面板 Settings 国内的资料比较少,大概很多人已经放弃了原生快捷方式,不过我打算通关原生快捷方式. 在配置面板中 IDE S ...
- Jenkins+tomcat+jdk setup
Jenkins download: http://jenkins-ci.org/ jdk version:jdk-7u45-linux-x64.tar.gz tomcat version:apache ...
- Java 并发专题 : Executor具体介绍 打造基于Executor的Webserver
转载标明出处:http://blog.csdn.net/lmj623565791/article/details/26938985 继续并发,貌似并发的文章非常少有人看啊~哈~ 今天准备具体介绍jav ...
- 树莓派学习笔记——USB wifi配置指南
0 前言 树莓派既能够使用有线网络又能够无线网络,假设使用有线网络不方便的话能够借助USB wifi无线网卡让树莓派也插上无线"翅膀". 可是和使用有线网络即插即用的方式不 ...
- 【quickhybrid】API多平台支撑的实现
前言 在框架规划时,就有提到过这个框架的一些常用功能需要支持H5环境下的调用,也就是需要实现API的多平台支撑 为什么要多平台支撑?核心仍然是复用代码,比如在微信下,在钉钉下,在quick容器下, 如 ...
- 自学Zabbix3.10.1.1-事件通知Notifications upon events-媒介类型email
自学Zabbix3.10.1.1-事件通知Notifications upon events-媒介类型email 配置媒介Email Administration→Media types->Cl ...
- HTTPS加密流程超详解(一)前期准备
0.前言 前一阵子想写一个HTTPS的嗅探工具,之前只是大致了解SSL/TLS协议的加密流程,真正上起手来一步一步分析发现还是有点复杂的,于是我参考了wireshark的源码以及各种RFC,弄清楚了S ...
- spring -boot s-tarter 详解
Starter POMs是可以包含到应用中的一个方便的依赖关系描述符集合.你可以获取所有Spring及相关技术的一站式服务,而不需要翻阅示例代码,拷贝粘贴大量的依赖描述符.例如,如果你想使用Sprin ...