7.2类和类型

现在读者可能对什么是类有了答题的感觉———或者已经有些不耐烦听我对他进行更多的介绍了。在开始介绍之前,先来认识一下什么是类,已经它和类型又有什么不同(或相同)

类到底是什么

从前面的部分中,类这个词已经多次出现,可以将他或多或少地视为种类或者类型的同义词。从很多方面说,这就是类--一种对象。所有的对象都属于某一个类,称为类的实例(instance)。例如,现在请往窗外看,鸟就是鸟类的实例。鸟类是一个非常通用(抽象)的类,具有很多子类:看到的鸟可能属于子类“百灵鸟”。可以将“鸟类”想象成所有鸟的集合,而“百灵鸟类”是其中的一个子集。当一个对象所属的类是另外一个对象所属类的子集时,前者就被称为后者的子类(subclass),所以“百灵鸟类”是鸟类的子类。相反,“鸟类”是“百灵鸟类”的超类(supclass)。

注意     正日常交谈中,可能经常用复数来描述对象的类,比如birds或者larks。Python中,习惯上都使用单数名词,并且首字母大写,比如Bird和Lark。

这样一比喻,子类和超类就容易理解了。但是在面向对象程序设计中,子类是隐式地,因为一个类的定义取决于他所支持的方法。类的所有势力都会包含这些方法,所以所有子类的所有实例都有这些方法。定义子类只是个定义更多(也有可能是重载已经存在的)的方法的过程。

例如,鸟类Bird可能支持fly方法,而企鹅类Penguin(Bird的子类)可能会增加个EatFish方法。当创建Penguin类时,可能会想要重写(override)超类的方法,对于Penguin的实例来说,这个方法要么什么也不做,要么就产生异常,因为Penguin(企鹅)不会fly(飞)。

7.2.2创建自己的类

终于来了!可以创建自己的类了!先来看一个简单地类:

_metaclass_ = type # 确定使用新式类

class Person:

def  setName(self ,name):

self.name = name

def getName(self):

return self.name

def  greet(self):

print"Hello,world! I‘m%s." %self.name

这个例子包含3个方法定义,除了他们是写在class语句之外,一切都好像是函数定义。Person当然是类的名字。class语句会在函数定义的地方创建自己的命名空间,一切看起来都挺好,但是那个self参数看起来有点奇怪。它是对于对象自身的引用。那么它是什么对象?让我们创建一些实例看看:

>>>foo = Person()

>>>bar = Person()

>>>foo.set.Name(‘LuKe Skywalker‘)

>>>bar.set.Name(‘Anakin Skywalker‘)

>>>foo.greet()

>>>Hello.world!I‘m Lukwalker.

>>>bar.greet()

Hello.world! I‘m Anakin Skywalker.

好了,例子一目了然,应该能说明self的用处了。在调用foo的setName和greet函数时,foo自动将自己作为第一个参数传入函数中——因此形象的命名为self。对于这个变量,每个人可能都会有自己的叫法,但是因为他总是对象自身,所以习惯上叫做self。

显然这就是self的用处和存在的必要性。没有他的话,成员方法就没法访问他们要对其特性进行操作的对象本身了。

和之前一样,特性是可以在外部访问的:

>>>foo.name

‘Luke Skywalker‘

>>>bar.name = ‘Yoda‘

>>>bar.greet()

Hello.world!I‘m Yoda.

7.2.3特性、函数和方法

self参数事实上正是方法和函数的区别。方法(更专业一点可以绑定方法)将他们的第一个参数绑定到所属的实例上,因此你无需显示提供该参数。当然也可以将特性绑定到一个普通函数上,这样就不会有特殊的self参数了:

>>>class Class:

def   method(self)

print ‘I have a self‘

>>>def function()

print"I don‘t..."

>>>instance  = Class()

>>>instance.method()

I have a self!

>>>instance.method = function

>>>instance.method()

I don‘t...

注意,self参数并不依赖于调用方法的方式,前面我们使用的是instance.method(实例.方法)的形式,可以随意使用其他变量引用同一个方法:

>>>class Bird:

song = ‘Squaak!‘

def sing(self):

print self .song

>>>bird = Bird()

>>>bird.sing()

Squaak!

>>>birdsong = bird.sing

>>>birdsong()

Squaak!

尽管最后一个方法调用看起来与函数调用十分相似,但是变量birdsong引用绑定方法bird.sing上,也就意味着着还是会对self参数进行访问(也就是说,他仍旧绑定到类的相同实例上)

再论私有化

默认情况下,程序可以从外部访问一个对象的特性。再次使用前面讨论过的有关封装的例子:

>>>c.name

‘Sir Lancelot‘

>>>c.name = ‘Sir Gumby‘

>>>c.getName()

‘Sir Gumny‘

有些程序员觉得这样做是可以的,但是有些人(比如SmallTalk之父,SmallTalk的对象特性只允许同一个对象的方法访问)觉得这样就破坏了封装的原则。他们认为对象的状态对于外部应该是完全隐藏的(不可访问)。有人可能觉得奇怪为什么他们会站在如此极端的立场上。每个对象管理自己的特性还不够吗?为什么要对外部世界隐藏呢?毕竟如果能直接使用ClosedObject的name特性的话就不用使用setName和getName方法了。

关键在于其他程序员可能不知道(可能也不应该知道)你的对象内部的具体操作。例如,ClosedOject可能会在其他对象更改自己的名字的时候,给一些管理员发送邮件消息。这应该是setName方法的一部分。但是如果直接使用c.name设定名字会发生什么?什么都没发生,Email也没有发出去。为了避免这类事件的发生,应该使用私有(private)特性,这是外部对象无法访问,但是getName和setName等访问器(accessor)能够访问的特性。

python并不直接支持私有方式,而要靠程序员自己把握在外部进行特性修改的时机。毕竟在使用对象前应该知道如何使用。但是,可以用一些小技巧达到私有特性的效果。

为了让方法或者特性变为私有(从外部无法访问),只要在他的名字前面加上双划线即可:

class Secretive:

def__inaccessible(self):

print "Bet you can‘t see me..."

def accessible(self):

print " The secret message is:"

self.__inaccessible()

现在_inaccessible从外界是无法访问的,而在类内部还能使用(比如从acessible)访问:

>>> s= Secretive()

>>>s._inaccssible()

Traceback(most recent call list):

File "<pyshell#112>", Line 1, in ?

s.inaccessible()

AttributeError: Secretive instance has no attribute ‘_inaccessible‘

>>>s.accessible()

The seret message is:

Bet you can‘t see me...

尽管双下划綫有些奇怪,但是看起来像是其他语言中的标准私有方法。真正发生的事情才是不标准的。类的内部定义中,所有以双下划线的名字都被“翻译”成前面加上单划线和类名的形式。

Secretive._Secretive_inaccessible

<unbound method Secretive._inaccessible>

在了解了这些幕后的事情后,实际上还是能在类外访问这些私有方法,尽管不应该这么做:

>>> s._Secretive__inaccessible()

Bet you can‘t see me ...

简而言之,确保其他人不会访问对象的方法和特性是不可能的,但是这类“名称变化术”就是他们不应该访问这些函数或者特性的强有力信号。

如果不需要使用这种方法,但是又不想其他数据访问内部数据,那么可以使用单下划线。这不过是个习惯,但的确有实际效果。例如前面有下划线的名字都不会被带星号的import语句(from moduel import*)导入。

7.2.4类的命名空间

下面的两个语句(几乎)等价:

def  foo(x): return x*x

foo = lambda x: x*x

两者都创建了返回参数平方的函数,而且都将变量foo绑定到函数上。变量foo可以在全局(模块)范围进行定义,也可以处于局部的函数或方法内。定义类时,同样的事情也会发生,所有位于class语句中的代码都在特殊的命名空间中执行——类命名空间(class namespace)。这个命名空间可由类内所有成员访问。并不是所有python程序员都知道类的定义其实就是执行代码块,这一点非常有用,比如,在类的定义区并不限定只能使用def语句:

>>>class C:

print  ‘Class C being defined...‘

Class C being defined...

>>>

看起来有点傻,但是看看下面的:

class MemberCouner:

members = 0

def init(self)

MemberCounter.members+=1

>>> m1 = MemberCounter()

>>> m1.init()

>>> MemberCounter.members

1

>>> m2 = MemberCounter()

>>> m2.init()

>>>MemberCounter.members

2

上面的代码中,在类作用域中定义了一个可供成员(实例)访问的变量,用来计算类的成员数量。注意init用来初始化所有实例:

就像方法一样,类作用域内的变量也可以被所有实例访问:

>>> m1.members

2

>>>m2.members

2

那么在实例中重绑定members特性呢?

>>>m1.members = ‘Two‘

>>>m1.members

‘Two‘

>>>m2.members

2

新numbers值被写到了m1的特性中,屏蔽了类范围内的变量。这根函数的局部和全局变量的行为十分类似。

时间: 2024-10-27 00:13:12

7.2类和类型的相关文章

WCF调试异常信息:ServiceHost 仅支持类服务类型

"/CommonHelpServices"应用程序中的服务器错误. ServiceHost 仅支持类服务类型. 说明: 执行当前 Web 请求期间,出现未经处理的异常.请检查堆栈跟踪信息,以了解有关该错误以及代码中导致错误的出处的详细信息. 异常详细信息: System.ArgumentException: ServiceHost 仅支持类服务类型. 源错误: 执行当前 Web 请求期间生成了未经处理的异常.可以使用下面的异常堆栈跟踪信息确定有关异常原因和发生位置的信息. 堆栈跟踪:

利用反射来获取继承的类的类型

在做web项目的时候,通常都有一个 BaseDao,BaseAction之类的,比如最近在学习一个 ssh项目,其中就要注入 dao,还有利用 模型驱动 来实现将 jsp页面封装成一个model,传到action中,因为表单,对应的 model也不同,所以要分别注入.这样就可以在定义 BaseAction 时,采用反射的方式,就可以自动获取继承类的类型了. public abstract class BaseAction<T> extends ActionSupport implements

【STL源码学习】std::list类的类型别名分析

有了点模板元编程的traits基础,看STL源码清晰多了,以前看源码的时候总被各种各样的typedef给折腾得看不下去, 将<list>头文件的类继承结构简化如下 #include <xmemory> #include <stdexcept> #define _STD_BEGIN namespace std { #define _STD_END } _STD_BEGIN // 第一个模板参数 template <class _Val_types> class

C# 类:类型 , 数学:类型

类(类型):   //.Length:获取字符串的长度,返回int型                        //.Trim去除字符串前后的空格                        //.TrimStart()去除前空格                        //.TrimEnd()去除后空格:                        //.ToUpper转换为大写:                        //.ToLower转换为小写:          

Python的类与类型

1.经典类与新式类 在了解Python的类与类型前,需要对Python的经典类(classic classes)与新式类(new-style classes)有个简单的概念. 在Python 2.x及以前的版本中,由任意内置类型派生出的类(只要一个内置类型位于类树的某个位置),都属于“新式类”,都会获得所有“新式类”的特性:反之,即不由任意内置类型派生出的类,则称之为“经典类”. “新式类”和“经典类”的区分在Python 3.x之后就已经不存在,在Python 3.x之后的版本,因为所有的类都

类与类型 获取类型几种方式!!

说到类型,JavaScript 定义了少量的数据类型: null undefined 布尔值 数字 字符串 函数 和 对象. 假如要区分值得类型,无疑 typeof 首选 .如下 1 typeof 1 2 typeof "string" 3 typeof true 4 typeof null 5 typeof undefined 6 typeof {} 7 typeof /\W/ 8 9 10 11 :1 number 12 :2 string 13 :3 boolean 14 :4

根据字符串生成类---类的类型.self---根据字符串创建控制器对象

swift和OC一样,都是通过NSClassFromString,根据一个字符串,生成相应的类. 1 // UITabBarButton是系统的私有类,不能直接使用 2 // if btn.isKind(of: UITabBarButton.self){ 3 if btn.isKind(of: NSClassFromString("UITabBarButton")!){ 4 // NSClassFromString:根据字符串取相应的类 5 } 取一个类的类型,oc中是[类 class

lua 中protobuf repeated 嵌套类 复合类型

PB基础知识科普 syntax = "proto2"; package PB; message Item { required string name = 1; } message Role { required string name = 1; optional string email = 2; repeated string t =3; repeated Item item1 = 4; optional Item item2 =5; } lua中解析 required  opti

Scala的类与类型

类和类型 List<String>和List<Int>类型是不一样的,但是jvm运行时会采用泛型擦除.导致List<String>和List<Int>都是Class<List>.为了得到正确的类型,需要通过反射. 泛型擦除 Java中的泛型基本上都是在编译器这个层次来实现的.在生成的Java字节码中是不包含泛型中的类型信息的.使用泛型的时候加上的类型参数,会在编译器在编译的时候去掉.这个过程就称为类型擦除.泛型擦除是为了兼容jdk1.5之前的jv

Python基础13_类与类型, MRO, C3算法, super()

一. python多继承 类与类型:http://www.cnblogs.com/blackmatrix/p/5594109.html 子类继承了多个父类, 当父类出现了重名方法时, 这时就涉及到查找父类方法的问题, 即MRO(method resolution order)问题 python中有两种类, 经典类和新式类 在Python2及以前的版本中,由任意内置类型派生出的类(只要一个内置类型位于类树的某个位置),都属于"新式类",都会获得所有"新式类"的特性:反