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把对象作为程序的基本单元,一个对象包含了数据和操作数据的函数. 面向过程的程序设计把计算机程 ...
随机推荐
- Js 调用 WebService 实例
Html页面代码: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/ ...
- 【hdu3367】Pseudoforest(伪森林)
http://acm.hdu.edu.cn/showproblem.php?pid=3367 题目大意 伪森林就是一个无向图,这个无向图有多个连通块且每个连通块只有一个简单环. 给你一个无向图,让你找 ...
- Git 二分调试法,火速定位疑难Bug!
你一定遇到过,一个很久没修改过的功能,莫名其妙的出现了问题?肉眼查代码.屡逻辑完全找不到问题点?前两天还好好的功能,怎么这个今天就不行了?这两天改动了这么多代码,到底是那一次改动引发的 Bug? 这样 ...
- Intent启动系统组件(activity,service,BroadReceiver)-android学习之旅(四十九)
android提供了统一的编程模型Intent来启动系统的组件,这样提供了松耦合性.是一种mvc的编程模式 $(function () { $('pre.prettyprint code').each ...
- hbase 0.98.1集群安装
本文将基于hbase 0.98.1解说其在linux集群上的安装方法,并对一些重要的设置项进行解释,本文原文链接:http://blog.csdn.net/bluishglc/article/deta ...
- nyist oj 756 重建二叉树
重建二叉树 时间限制:1000 ms | 内存限制:65535 KB 难度:3 描写叙述 题目非常easy.给你一棵二叉树的后序和中序序列,求出它的前序序列(So easy!). 输入 输入有多组 ...
- Linux网络编程--wireshark分析TCP包头的格式
摘要: 本文简介了TCP面向连接理论知识,具体讲述了TCP报文各个字段含义.并从Wireshark俘获分组中选取TCP连接建立相关报文段进行分析. 一.概述 TCP是面向连接的可靠传输 ...
- 游戏服务端中使用Servlet和Java注解的一个好设计
SNS类游戏基本都是使用HTTP短连接,用Java来开发服务端时能够使用Servlet+Tomcat非常轻松的架构起服务端来.在这里介绍一种使用Servlet比較好的一种设计,我也见过非常多基于HTT ...
- Hibernate(或其它ORM)里的inverse用法详解,内容摘自Java web轻量级开发面试教程
本文来是从 java web轻量级开发面试教程从摘录的. Inverse的英文含义是反转,在Hibernate中用来决定是由哪方来维护两个业务实体类之间的关联关系,具体而言,就是由哪方去设置这个被外键 ...
- iOS开发之JSON转PLIST(把存储json格式的文件转换成plist文件)
p.p1 { margin: 0.0px 0.0px 0.0px 60.0px; font: 16.0px "PingFang SC"; color: #586e75 } p.p2 ...