python和php的面向对象
2015-11-14 18:03
681 查看
python和php的面向对象
OOP术语的定义
类:对具有相同数据和方法的一组对象的描述或定义。
对象:对象是一个类的实例。
实例(instance):一个对象的实例化实现。
标识(identity):每个对象的实例都需要一个可以唯一标识这个实例的标记。
实例属性(instance attribute):一个对象就是一组属性的集合。
实例方法(instance method):所有存取或者更新对象某个实例一条或者多条属性的函数的集合。
类属性(classattribute):属于一个类中所有对象的属性,不会只在某个实例上发生变化
类方法(classmethod):那些无须特定的对性实例就能够工作的从属于类的函数。
关注一个语言的OPP,主要关注:
1、元素概念
2、各元素在类内(实例符、类符)外(类名)和其它域引用方法及特性(在哪定义、在哪用、是否可以定义静态属性)
3、类特性:构造、析构、权限控制(封装性)、继承、多态性(覆盖与重载)、魔术方法、自省与反射、接口与抽象类
Python的面向对象
关键元素(成员方法 成员变量)
Python中定义类的方式比较简单:
class 类名:
类变量
def __init__(self,paramers):
def 函数(self,...)
……
在Python类中定义的成员方法通常有三种:
实例方法,类方法以及静态方法。实例方法一般都以self作为第一个参数,必须和具体的对象实例进行绑定才能访问,类方法以cls作为第一个参数,cls表示类本身,定义时使用@classmethod
静态方法不需要默认的任何参数,跟一般的普通函数类似.定义的时候使用@staticmethod。
因为各个方法传入的参数不一样,这样定义的权限就更加清晰,比如说类方法传入cls,自然不能定义和访问实例变量self.x。
成员变量包括类变量和实例变量。而在类的方法中定义的变量叫实例变量。
关键元素有以下几种:
类变量(所有方法外、类方法cls.x)
实例变量(方法中self.x)不加self则只是该方法内部局部变量。有了self就有了全局属性,在类的内
部和外部都可以访问了
实例方法
类方法
静态方法
其中类变量和类方法、静态方法属于静态属性(任何一个对象的销毁都不会释放,直到进程退出)。不经实例化就可以使用。会影响所属类的所有对象
实例化与元素引用
new一个实例就是在类名后面加()即可 obj = class_name()
引用符号只有一个.
self 在类的内部表示实例本身。cls表示类本身。
成员四个引用源:类名、cls、实例名、self、
前两者只能引用静态成员。
类变量的定义与引用
类变量在所有的方法外面 x= ,也可以在类的内部任何可以引用它的地方定义,甚至在类的外部也可以。一经定义,就是类的属性。使用前注意一定要提前定义。为了维护方便,尽量只在所有方法的外面初始化,杜绝外部赋值。
1、在类的内部直接使用类名.类变量的方式引用。
2、在类的内部如果没有同名的实例变量,在实例方法中,self.x也可以搜索到。但如果改动的话,或者有同名的实例变量的话则会覆盖类变量。所以最好是self.__class__.x
3、在类的内部,类方法中可以使用cls.x引用。
4、在类的外部,类名.类变量引用,实例.__class__.x引用,在没有同名实例变量的时候也可以
使用实例.x引用
类变量的改动影响当前进程中所有文件所使用的该类和类的实例。
class A:
a1 = 1
def echo(self):
A.a1 += 1
def echo2(self):
self.a1 += 1 (不建议这样使用,相当于搜索类变量,然后初始化一个实例变量,这样即使
是类变量受影响变了,也通过实例索引不到了)
def echo3(self):
self.__class__.a1 += 1
@classmethod
def echo4(cls):
print cls.a1
obj = A()
obj.echo()
print obj.a1
print A.a1
obj.echo2()
print obj.a1
print A.a1
obj.echo3()
print obj.a1
print A.a1
2
2
3
2
3
3
实例变量的定义和引用
实例变量定义在实例方法中,即入参带有self的方法。甚至是类的外部使用实例.定义(避免这样用)。self.x 一经定义,则是类的属性。可以在任何实例方法中定义,使用前一定要定义过。在外部实例化后才能生效。
1、在类的内部,实例方法中使用self.x定义和引用
2、在类的外部,实例.x引用
实例变量的变动只影响当前实例
class A:
def __init__(self,a):
self.name = a
def echo(self):
self.name = "regou"
self.name2 = 'juejiang'
def echo2(self):
if self.name2 == 'juejiang':
print 'here'
obj = A('wangwei')
print obj.name
#print obj.name2 报错
obj.echo()
print obj.name
print obj.name2
obj.echo2()
实例方法的引用
实例方法以def func(self) 定义在类中。在内部实例化方法中可以使用。在外部只有实例化以后才能调用。
1、在类的内部,实例方法中使用self.x引用
2、在类的外部,实例.x引用
class A:
def __init__(self,a):
self.name = a
def echo(self):
self.name = "regou"
def echo2(self):
self.echo()
@staticmethod
def echo3():
self.echo()
a = A('wangwei')
#a.echo3() 报错
a.echo2()
print a.name
静态方法和类方法的引用
静态方法冠以@staticmethod定义在类中。不经过实例化就可以使用,不传入cls也没有self,与函数没有区别,只是放在了类的内部。方便归类以及与类的其他功能配合使用。因为没有传self,不用担心它会调用实例化后才能使用的东西。
1、在类的内部,使用类名.静态方法()引用
2、在类的内部,实例方法中,使用self.__classs.静态方法()引用 或者self.静态方法()
3、在类的内部,类方法中,使用cls.引用
4、在类的外部,类名.静态方法引用,实例.__class__.x引用,也可以实例.x引用
class A:
test_var = 99
def echo(self):
A.echo2()
self.__class__.echo2()
self.echo2()
@staticmethod
def echo2():
print A.test_var
@classmethod
def echo3(cls):
cls.echo2()
print cls.test_var
obj = A()
obj.echo()
obj.echo2()
obj.__class__.echo2()
A.echo2()
A.echo3()
访问控制
Python的访问控制相对简单,没有public(内外 可继承),private(类内 不可继承),protected(类内 可继承)等属性,python认为用户在访问对象的属性的时候是明确自己在做什么的,因此认为私有数据不是必须的,但是如果你必须实现数据隐藏即私有数据,也是可以的。事实上,即使是private的,Python也不能阻止你从外部访问它对Python来说,似乎约定(convention)更重要些,大部分的规则都是通过约定来表达,而不是从机制(mechanism)上去保证。
构造与析构
惨绝人寰,如果在子类中不显示的调用父类的构造函数,则不会自动执行父类的。
class A:
def __init__(self):
print "create a"
class B(A):
def __init__(self):
print "create b"
obj = B()
只会打印create b
class A:
def __init__(self):
print "create a"
class B(A):
def __init__(self):
print "create b"
A.__init__(self)
obj = B()
关于构造函数,在继承中有几条法则:
1、如果子类没有定义自己的构造函数,父类的构造函数会被默认调用,但是此时如果要实例化子类的对象,则只能传入父类的构造函数对应的参数,否则会出错
2、如果子类定义了自己的构造函数,而没有显示调用父类的构造函数,则父类的属性不会被初始化,显示调用父类,子类和父类的属性都会被初始化
Python由于具有垃圾回收机制,通常不需要用户显示的去调用析构函数(这话说的 ,构造和析构本来就不用显示调用才对),即使调用,实例也不会立即释放,而是到该实例对象所有的引用都被清除掉后才会执行。
>>>class P:
def__del__(self):
print"deleted"
>>>class S(P):
def__init__(self):
print'initialized'
def__del__(self):
P.__del__(self)
print"child deleted"
>>>a=S()
initialized
>>>b=a
>>>c=a
>>>id(a),id(b),id(c)
(18765704,18765704, 18765704)
>>>del a
>>>del b
>>>del c
deleted
childdeleted
>>>
继承、多态性(覆盖与重载)
Python同时支持单继承与多继承,继承的基本语法为class新类名(父类1,父类2,..),当只有一个父类时为单继承,当存在多个父类时为多继承。子类会继承父类的所有的属性和方法,子类也可以覆盖父类同名的变量和方法。在传统类中,在多继承中如果子类和父类中同名的方法或者属性,在查找的时候基本遵循自左到右,深度优先的原则。
多态性:根据引用相同的对象,但表现不同。多态存在两种形式,覆盖和重载。所谓的覆盖就是在子类中重写父类的方法。函数重载是指一个标示符用作多个函数名,且能通过函数参数的个数或参数类型区分开来。(多个函数的名称以及返回值类型均相同,仅参数类型或参数个数不同)
python没提供函数重载的方法,因为:
为了考虑为什么 python 不提供函数重载,首先我们要研究为什么需要提供函数重载。函数重载主要是为了解决两个问题。1。可变参数类型。2。可变参数个数。另外,一个基本的设计原则是,仅仅当两个函数除了参数类型和参数个数不同以外,其功能是完全相同的,此时才使用函数重载,如果两个函数的功能其实不同,那么不应当使用重载,而应当使用一个名字不同的函数。好吧,那么对于情况 1 ,函数功能相同,但是参数类型不同,python 如何处理?答案是根本不需要处理,因为
python 可以接受任何类型的参数,如果函数的功能相同,那么不同的参数类型在 python 中很可能是相同的代码,没有必要做成两个不同函数。那么对于情况 2 ,函数功能相同,但参数个数不同,python 如何处理?大家知道,答案就是缺省参数。对那些缺少的参数设定为缺省参数即可解决问题。因为你假设函数功能相同,那么那些缺少的参数终归是需要用的。
静态语言需要 function overloading 主要是为了解决调用灵活性的问题,Python 这么灵活的脚本语言用 function overloading 纯属多此一举
个人以为,一个语言选择重载或者可选参数两者中的一种就是了
自省与反射
两个概念认为是相似的。用于实现在运行时获取未知对象的信息。面向对象的编程中对象被赋予了自省的能力,而这个自省的过程就是反射.通过使用反射api就能够实现动态的获 取一个类所有属性和方法以及调用该方法和属性. 自省是“道”,反射是“术”。
比如说我想动态调用一个类的方法,或者我想知道我的一个类到底有哪些方法,有哪些属性。这就叫做反射。比如有时候你想知道类的方法的是private还是public,只能靠反射了。你想知道某个函数的注释是什么,只能靠反射了。简单的说就是能让你进入类,对象,函数等语言元素的内部。
举一个例子,是反射在工厂模式中的应用:
class LazyImport:
def __init__(self, module_name):
self.module_name = module_name
self.module = None
def __getattr__(self, funcname):
if self.module is None:
self.module = __import__(self.module_name)
print(self.module)
return getattr(self.module, funcname)
string = LazyImport('string')
print(string.ascii_lowercase)
几个重要的API
dir: 列出一个对象所有的方法和属性
>>> a = [1,2,3]
>>> dir(a)
['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__delslice__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getslice__', '__gt__', '__hash__', '__iadd__', '__imul__',
'__init__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__setslice__', '__sizeof__', '__str__', '__subclasshook__', 'append',
'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', ‘sort']
当你对一个你构造的对象使用dir()时,可能会发现列表中的很多属性并不是你定义的。这些属性一般保存了对象的元数据,比如类的__name__属性保存了类名。大部分这些属性都可以修改,不过改动它们意义并不是很大
getattr方法,传入参数是对象和该对象的函数或者属性的名字,返回对象的函数或者属性实例
getattr(a,'sort')
<built-in method sort of list object at 0x1093dfe18>
hasattr(obj, attr): 这个方法用于检查obj是否有一个名为attr的值的属性,返回一个布尔值。
>>> hasattr(a,'sort')
True
setattr(obj, attr, val):
调用这个方法将给obj的名为attr的值的属性赋值为val。例如如果attr为'bar',则相当于obj.bar = val。
callable方法,如果传入的参数是可以调用的函数,则返回true,否则返回false
>>> callable(getattr(a,'sort'))
True
下面这段代码列出对象所有函数:
methodList = [method for method in dir(object) if callable(getattr(object,method))]
globals()和locals()返回的是全局和局部的名字空间。是map,名字:对象。
isinstance(object, classinfo) 检查object是不是classinfo中列举出的类型,返回布尔值
>>> isinstance(a,list)
True
>>> type(a)
<type 'list'>
>>> type(a) == list
True
issubclass判断类参数class是否是类型参数classinfo的子类
class Line:
pass
class RedLine(Line):
pass
class Rect:
pass
print(issubclass(RedLine, Line))
print(issubclass(Rect, Line))
inspect模块提供了一系列函数用于帮助使用自省。下面仅列出较常用的一些函数
检查对象类型
is{module|class|function|method|builtin}(obj):
检查对象是否为模块、类、函数、方法、内建函数或方法。
isroutine(obj):
用于检查对象是否为函数、方法、内建函数或方法等等可调用类型。用这个方法会比多个is*()更方便,不过它的实现仍然是用了多个is*()。
接口与抽象类
接口相当于定义了一个类的样子(所拥有的属性和方法)
抽象类(包含一个或多个抽象方法的类),不能被直接实例化,必须被继承,也就是说它要求子类必须实现某些方法。
从某种意义上来说,他们具有相似的含义。抽象类可以当做接口使用。
由于python 没有抽象类和借口的概念,所以要实现这种功能得abc.py 这个类库
from abc import ABCMeta, abstractmethod
class A:
__metaclass__ = ABCMeta
def __init__(self):
pass
@abstractmethod
def echo(self):
pass
class B(A):
def echo(self,a):
print a
obj = B()
obj.echo(‘abc')
可以看出python 抽象类比较简陋,要求不严格,连抽象方法的个数也不要求。
PHP的面向对象
关键元素(成员方法 成员变量 类常量)
<?php
class a{
const CON="wangwei";
public $var = a;
public function func(){
echo "in func a";
}
}
成员方法
成员变量
类常量
类成员是不是静态的(静态属性,不经过实例化就可以用),即python里的类变量(方法外,看位置)和类方法(classmethod修饰),只看有没有static修饰。static代表的静态意味着整个进程的持续存储的能力。包括局部静态变量。
使用static修饰的成员变量相当于python的类变量。
class abc{
public function func(){
static $b = 0;
$b += 1;
echo $b;
}
public static function func2(){
}
}
$obj = new abc();
$obj2 = new abc();
$obj->func();
$obj->func();
$obj->func();
$obj2->func();
$obj2->func();
$obj2->func();
123456
实例化与元素引用
$obj = new abc();
引用源5个:类名 self 实例名 $this parent
前两者和最后一个,只能用来引用静态成员。
在类的内部 self表示类本身。$this表示实例本身。parent表示父类和父类对象(使用parent::引用父类的所有方法,无论是否静态)。
引用符有 ->和::两种,分别用来引用普通成员和静态成员。
php的成员方法没有符号绑定,只能做硬性限制。即静态方法中不可以使用$this引用普通成员。这比较好理解。静态方法不经实例化就可以使用,而$this则是代表实例。
成员变量的定义与引用
普通成员变量(实例变量)
可以在所有方法的外部定义(使用这种),在普通成员方法中定义,甚至可以在类外部定义
可以在类的内部使用$this->引用,在类的外部使用实例->引用。
<?php
class abc{
public $var = 1;
public function func(){
echo $this->var;
$this->var2 = 2;
}
public function func2(){
echo $this->var2;
}
}
$obj = new abc();
$obj->func();
$obj->func2();
echo $obj->var2;
静态成员变量(类变量)
只可以在所有方法的外部定义
在类的类的内部使用self:: $this:: abc:: 引用。在外部使用实例名:: 和类名::引用
<?php
class abc{
public static $var = 1;
public function func(){
echo self::$var;
echo $this::$var;
echo abc::$var;
}
}
$obj = new abc();
$obj->func();
echo $obj::$var;
echo abc::$var;
静态成员变量的改变会影响这个类的所有实例。
class abc{
public $var = 1;
public static $var2 = 2;
public function func(){
$this->var = 3;
self::$var2 = 4;
}
}
$obj = new abc();
$obj2 = new abc();
$obj->func();
echo $obj->var;
echo $obj::$var2;
echo $obj2->var;
echo $obj2::$var2;
class abc{
public static $b = 0;
public function func(){
self::$b += 1;
echo self::$b;
}
public static function func2(){
}
}
$obj = new abc();
$obj2 = new abc();
$obj->func();
$obj2->func();
成员方法的引用
普通成员方法(实例方法)
可以在类的内部使用$this->引用,在类的外部使用实例->引用。
class abc{
public $var = 1;
public function func(){
print $this->var;
}
public function func2(){
print $this->var;
$this->func();
}
}
$obj = new abc();
$obj->func2();
静态成员方法(类方法)
在类的内部使用self:: abc:: 引用。在外部使用实例名:: 和类名::引用
class abc{
public static $var = 1;
public static function func(){
echo self::$var;
}
public static function func2(){
self::func();
abc::func();
}
}
$obj = new abc();
$obj::func2();
abc::func2();
类常量的定义与访问(值不可改变)
在类的内部使用self:: abc:: 引用。在外部使用实例名:: 和类名::引用
class abc{
const CVAR = 9;
public function func(){
echo self::CVAR;
echo abc::CVAR;
}
}
$obj = new abc();
$obj->func();
echo $obj::CVAR;
echo abc::CVAR;
访问控制
面向对象的一大特性是封装性,即数据隐藏。数据隐藏的对象是:成员变量、成员方法。而类常量是不可隐藏的。隐藏的方法是加关键字:
Public,private,protected
Public:这是默认的类型。公开的。可以随意调用。子类可以集成父类所有的公共成员。
Private:只能在类内,不能在类外调用(即实例化后或者调用静态),也不可被子类继承。
Protected:只能在类内和子类内部调用,是私有数据的折中方案。
class abc{
public function func(){
echo "public func ";
}
private static function func2(){
//私有或者受保护类型定义成静态没有意思 因为外部不可访问
echo "private func ";
}
protected function func3(){
echo "protected func ";
}
public function use_func(){
$this->func();
self::func2();
$this->func3();
}
}
$obj = new abc();
$obj->use_func();
$obj->func();
//PHP Fatal error: Call to private method abc::func2() from context
//$obj->func2();
//PHP Fatal error: Call to protected method abc::func3()
//$obj->func3();
class cd extends abc{
public function sub_func(){
$this->func();
//PHP Fatal error: Call to private method abc::func2() from context
//$this->func2();
$this->func3();
}
}
$obj = new cd();
$obj->sub_func();
$obj->func();
构造与析构
惨绝人寰,如果在子类中不显示的调用父类的构造函数,则不会自动执行父类的。(也难怪,没有理由要求,参数同步)
如果子类没有构造方法,则父类的构造方法会自动执行
class abc{
public function __construct($name,$age){
$this->name = $name;
$this->age = $age;
}
}
class cd extends abc{
}
$obj = new cd('wangwei',25);
echo $obj->name,$obj->age;
如果有的话,则需要显式调用,parent只代表自己的直属父类
class origin {
public function __construct($name,$age){
$this->name_or = $name;
$this->age_or = $age;
}
}
class abc extends origin{
public function __construct($name,$age){
$this->name = $name;
$this->age = $age;
parent::__construct($name,13);
}
}
class cd extends abc{
public function __construct($name){
$this->sub_name = $name;
parent::__construct($name,12);
}
}
$obj = new cd('wangwei');
echo $obj->name,$obj->age;
echo $obj->name_or,$obj->age_or;
继承、多态性(覆盖与重载)
在大多数人看来,多继承并不是一种好的设计方法。php只支持单继承。
无论是调用子类的某个方法、还是在父类中调用某一个方法,都是子类同名(同性、即要么非静态要么静态否则报错)方法覆盖父类的。可以使用parent::直接调用父类。
class abc {
public function func(){
echo 'parent';
}
public function use_func(){
$this->func();
}
}
class cd extends abc{
public function func(){
parent::func();
echo 'subclass';
}
}
$obj = new cd();
$obj->use_func();
parentsubclass
使用__call魔术方法来实现重载。
class A{
public function __call($name, $num){
var_dump($name);
var_dump(count($num));
$func_name = $name.count($num);
$this->$func_name();
}
public function func0(){
echo 'haha';
}
}
$obj = new A();
$obj->func();
自省与反射
反射api是php内建的oop技术扩展,包括一些类,异常和接口,综合使用他们可用来帮助我们分析其它类,接口,方法,属性,方法和扩展。这些oop扩展被称为反射。
通过ReflectionClass,我们可以得到类的以下信息:
1)常量 Contants
2)属性 Property Names
3)方法 Method Names
5)命名空间 Namespace
6)Person类是否为final或者abstract
/article/1208363.html
http://php.net/manual/en/book.reflection.php
/article/8174559.html
接口与抽象类
PHP 5 支持抽象类和抽象方法。定义为抽象的类不能被实例化。任何一个类,如果它里面至少有一个方法是被声明为抽象的,那么这个类就必须被声明为抽象的。被定义为抽象的方法只是声明了其调用方式(参数),不能定义其具体的功能实现。
继承一个抽象类的时候,子类必须定义父类中的所有抽象方法;另外,这些方法的访问控制必须和父类中一样(或者更为宽松)。
abstract class AbstractClass
{
// 强制要求子类定义这些方法
abstract protected function getValue();
abstract protected function prefixValue($prefix);
// 普通方法(非抽象方法)
public function printOut() {
print $this->getValue() . "\n";
}
}
class ConcreteClass1 extends AbstractClass
{
protected function getValue() {
return "ConcreteClass1";
}
public function prefixValue($prefix) {
return "{$prefix}ConcreteClass1";
}
}
使用接口(interface),你可以指定某个类必须实现哪些方法,但不需要定义这些方法的具体内容。我们可以通过interface来定义一个接口,就像定义一个标准的类一样,但其中定义所有的方法都是空的。接口中定义的所有方法都必须是public,这是接口的特性。
实现
要实现一个接口,可以使用implements操作符。类中必须实现接口中定义的所有方法,否则 会报一个fatal错误。如果要实现多个接口,可以用逗号来分隔多个接口的名称。
Note:
实现多个接口时,接口中的方法不能有重名。
Note:
接口也可以继承,通过使用extends操作符。
常量
接口中也可以定义常量。接口常量和类常量的使用完全相同。 它们都是定值,不能被子类或子接口修改。
/ 声明一个'iTemplate'接口
interface iTemplate
{
const CONST_VAR = 'CONTST';
public function setVariable($name, $var);
public function getHtml($template);
}
// 实现接口
class Template implements iTemplate
{
private $vars = array();
public function setVariable($name, $var)
{
$this->vars[$name] = $var;
}
public function getHtml($template)
{
return $this->vars;
}
}
$obj = new Template();
$obj->setVariable('name','wangwei');
var_dump($obj->getHtml(''));
echo $obj::CONST_VAR;
OOP术语的定义
类:对具有相同数据和方法的一组对象的描述或定义。
对象:对象是一个类的实例。
实例(instance):一个对象的实例化实现。
标识(identity):每个对象的实例都需要一个可以唯一标识这个实例的标记。
实例属性(instance attribute):一个对象就是一组属性的集合。
实例方法(instance method):所有存取或者更新对象某个实例一条或者多条属性的函数的集合。
类属性(classattribute):属于一个类中所有对象的属性,不会只在某个实例上发生变化
类方法(classmethod):那些无须特定的对性实例就能够工作的从属于类的函数。
关注一个语言的OPP,主要关注:
1、元素概念
2、各元素在类内(实例符、类符)外(类名)和其它域引用方法及特性(在哪定义、在哪用、是否可以定义静态属性)
3、类特性:构造、析构、权限控制(封装性)、继承、多态性(覆盖与重载)、魔术方法、自省与反射、接口与抽象类
Python的面向对象
关键元素(成员方法 成员变量)
Python中定义类的方式比较简单:
class 类名:
类变量
def __init__(self,paramers):
def 函数(self,...)
……
在Python类中定义的成员方法通常有三种:
实例方法,类方法以及静态方法。实例方法一般都以self作为第一个参数,必须和具体的对象实例进行绑定才能访问,类方法以cls作为第一个参数,cls表示类本身,定义时使用@classmethod
静态方法不需要默认的任何参数,跟一般的普通函数类似.定义的时候使用@staticmethod。
因为各个方法传入的参数不一样,这样定义的权限就更加清晰,比如说类方法传入cls,自然不能定义和访问实例变量self.x。
成员变量包括类变量和实例变量。而在类的方法中定义的变量叫实例变量。
关键元素有以下几种:
类变量(所有方法外、类方法cls.x)
实例变量(方法中self.x)不加self则只是该方法内部局部变量。有了self就有了全局属性,在类的内
部和外部都可以访问了
实例方法
类方法
静态方法
其中类变量和类方法、静态方法属于静态属性(任何一个对象的销毁都不会释放,直到进程退出)。不经实例化就可以使用。会影响所属类的所有对象
实例化与元素引用
new一个实例就是在类名后面加()即可 obj = class_name()
引用符号只有一个.
self 在类的内部表示实例本身。cls表示类本身。
成员四个引用源:类名、cls、实例名、self、
前两者只能引用静态成员。
类变量的定义与引用
类变量在所有的方法外面 x= ,也可以在类的内部任何可以引用它的地方定义,甚至在类的外部也可以。一经定义,就是类的属性。使用前注意一定要提前定义。为了维护方便,尽量只在所有方法的外面初始化,杜绝外部赋值。
1、在类的内部直接使用类名.类变量的方式引用。
2、在类的内部如果没有同名的实例变量,在实例方法中,self.x也可以搜索到。但如果改动的话,或者有同名的实例变量的话则会覆盖类变量。所以最好是self.__class__.x
3、在类的内部,类方法中可以使用cls.x引用。
4、在类的外部,类名.类变量引用,实例.__class__.x引用,在没有同名实例变量的时候也可以
使用实例.x引用
类变量的改动影响当前进程中所有文件所使用的该类和类的实例。
class A:
a1 = 1
def echo(self):
A.a1 += 1
def echo2(self):
self.a1 += 1 (不建议这样使用,相当于搜索类变量,然后初始化一个实例变量,这样即使
是类变量受影响变了,也通过实例索引不到了)
def echo3(self):
self.__class__.a1 += 1
@classmethod
def echo4(cls):
print cls.a1
obj = A()
obj.echo()
print obj.a1
print A.a1
obj.echo2()
print obj.a1
print A.a1
obj.echo3()
print obj.a1
print A.a1
2
2
3
2
3
3
实例变量的定义和引用
实例变量定义在实例方法中,即入参带有self的方法。甚至是类的外部使用实例.定义(避免这样用)。self.x 一经定义,则是类的属性。可以在任何实例方法中定义,使用前一定要定义过。在外部实例化后才能生效。
1、在类的内部,实例方法中使用self.x定义和引用
2、在类的外部,实例.x引用
实例变量的变动只影响当前实例
class A:
def __init__(self,a):
self.name = a
def echo(self):
self.name = "regou"
self.name2 = 'juejiang'
def echo2(self):
if self.name2 == 'juejiang':
print 'here'
obj = A('wangwei')
print obj.name
#print obj.name2 报错
obj.echo()
print obj.name
print obj.name2
obj.echo2()
实例方法的引用
实例方法以def func(self) 定义在类中。在内部实例化方法中可以使用。在外部只有实例化以后才能调用。
1、在类的内部,实例方法中使用self.x引用
2、在类的外部,实例.x引用
class A:
def __init__(self,a):
self.name = a
def echo(self):
self.name = "regou"
def echo2(self):
self.echo()
@staticmethod
def echo3():
self.echo()
a = A('wangwei')
#a.echo3() 报错
a.echo2()
print a.name
静态方法和类方法的引用
静态方法冠以@staticmethod定义在类中。不经过实例化就可以使用,不传入cls也没有self,与函数没有区别,只是放在了类的内部。方便归类以及与类的其他功能配合使用。因为没有传self,不用担心它会调用实例化后才能使用的东西。
1、在类的内部,使用类名.静态方法()引用
2、在类的内部,实例方法中,使用self.__classs.静态方法()引用 或者self.静态方法()
3、在类的内部,类方法中,使用cls.引用
4、在类的外部,类名.静态方法引用,实例.__class__.x引用,也可以实例.x引用
class A:
test_var = 99
def echo(self):
A.echo2()
self.__class__.echo2()
self.echo2()
@staticmethod
def echo2():
print A.test_var
@classmethod
def echo3(cls):
cls.echo2()
print cls.test_var
obj = A()
obj.echo()
obj.echo2()
obj.__class__.echo2()
A.echo2()
A.echo3()
访问控制
Python的访问控制相对简单,没有public(内外 可继承),private(类内 不可继承),protected(类内 可继承)等属性,python认为用户在访问对象的属性的时候是明确自己在做什么的,因此认为私有数据不是必须的,但是如果你必须实现数据隐藏即私有数据,也是可以的。事实上,即使是private的,Python也不能阻止你从外部访问它对Python来说,似乎约定(convention)更重要些,大部分的规则都是通过约定来表达,而不是从机制(mechanism)上去保证。
构造与析构
惨绝人寰,如果在子类中不显示的调用父类的构造函数,则不会自动执行父类的。
class A:
def __init__(self):
print "create a"
class B(A):
def __init__(self):
print "create b"
obj = B()
只会打印create b
class A:
def __init__(self):
print "create a"
class B(A):
def __init__(self):
print "create b"
A.__init__(self)
obj = B()
关于构造函数,在继承中有几条法则:
1、如果子类没有定义自己的构造函数,父类的构造函数会被默认调用,但是此时如果要实例化子类的对象,则只能传入父类的构造函数对应的参数,否则会出错
2、如果子类定义了自己的构造函数,而没有显示调用父类的构造函数,则父类的属性不会被初始化,显示调用父类,子类和父类的属性都会被初始化
Python由于具有垃圾回收机制,通常不需要用户显示的去调用析构函数(这话说的 ,构造和析构本来就不用显示调用才对),即使调用,实例也不会立即释放,而是到该实例对象所有的引用都被清除掉后才会执行。
>>>class P:
def__del__(self):
print"deleted"
>>>class S(P):
def__init__(self):
print'initialized'
def__del__(self):
P.__del__(self)
print"child deleted"
>>>a=S()
initialized
>>>b=a
>>>c=a
>>>id(a),id(b),id(c)
(18765704,18765704, 18765704)
>>>del a
>>>del b
>>>del c
deleted
childdeleted
>>>
继承、多态性(覆盖与重载)
Python同时支持单继承与多继承,继承的基本语法为class新类名(父类1,父类2,..),当只有一个父类时为单继承,当存在多个父类时为多继承。子类会继承父类的所有的属性和方法,子类也可以覆盖父类同名的变量和方法。在传统类中,在多继承中如果子类和父类中同名的方法或者属性,在查找的时候基本遵循自左到右,深度优先的原则。
多态性:根据引用相同的对象,但表现不同。多态存在两种形式,覆盖和重载。所谓的覆盖就是在子类中重写父类的方法。函数重载是指一个标示符用作多个函数名,且能通过函数参数的个数或参数类型区分开来。(多个函数的名称以及返回值类型均相同,仅参数类型或参数个数不同)
python没提供函数重载的方法,因为:
为了考虑为什么 python 不提供函数重载,首先我们要研究为什么需要提供函数重载。函数重载主要是为了解决两个问题。1。可变参数类型。2。可变参数个数。另外,一个基本的设计原则是,仅仅当两个函数除了参数类型和参数个数不同以外,其功能是完全相同的,此时才使用函数重载,如果两个函数的功能其实不同,那么不应当使用重载,而应当使用一个名字不同的函数。好吧,那么对于情况 1 ,函数功能相同,但是参数类型不同,python 如何处理?答案是根本不需要处理,因为
python 可以接受任何类型的参数,如果函数的功能相同,那么不同的参数类型在 python 中很可能是相同的代码,没有必要做成两个不同函数。那么对于情况 2 ,函数功能相同,但参数个数不同,python 如何处理?大家知道,答案就是缺省参数。对那些缺少的参数设定为缺省参数即可解决问题。因为你假设函数功能相同,那么那些缺少的参数终归是需要用的。
静态语言需要 function overloading 主要是为了解决调用灵活性的问题,Python 这么灵活的脚本语言用 function overloading 纯属多此一举
个人以为,一个语言选择重载或者可选参数两者中的一种就是了
自省与反射
两个概念认为是相似的。用于实现在运行时获取未知对象的信息。面向对象的编程中对象被赋予了自省的能力,而这个自省的过程就是反射.通过使用反射api就能够实现动态的获 取一个类所有属性和方法以及调用该方法和属性. 自省是“道”,反射是“术”。
比如说我想动态调用一个类的方法,或者我想知道我的一个类到底有哪些方法,有哪些属性。这就叫做反射。比如有时候你想知道类的方法的是private还是public,只能靠反射了。你想知道某个函数的注释是什么,只能靠反射了。简单的说就是能让你进入类,对象,函数等语言元素的内部。
举一个例子,是反射在工厂模式中的应用:
class LazyImport:
def __init__(self, module_name):
self.module_name = module_name
self.module = None
def __getattr__(self, funcname):
if self.module is None:
self.module = __import__(self.module_name)
print(self.module)
return getattr(self.module, funcname)
string = LazyImport('string')
print(string.ascii_lowercase)
几个重要的API
dir: 列出一个对象所有的方法和属性
>>> a = [1,2,3]
>>> dir(a)
['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__delslice__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getslice__', '__gt__', '__hash__', '__iadd__', '__imul__',
'__init__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__setslice__', '__sizeof__', '__str__', '__subclasshook__', 'append',
'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', ‘sort']
当你对一个你构造的对象使用dir()时,可能会发现列表中的很多属性并不是你定义的。这些属性一般保存了对象的元数据,比如类的__name__属性保存了类名。大部分这些属性都可以修改,不过改动它们意义并不是很大
getattr方法,传入参数是对象和该对象的函数或者属性的名字,返回对象的函数或者属性实例
getattr(a,'sort')
<built-in method sort of list object at 0x1093dfe18>
hasattr(obj, attr): 这个方法用于检查obj是否有一个名为attr的值的属性,返回一个布尔值。
>>> hasattr(a,'sort')
True
setattr(obj, attr, val):
调用这个方法将给obj的名为attr的值的属性赋值为val。例如如果attr为'bar',则相当于obj.bar = val。
callable方法,如果传入的参数是可以调用的函数,则返回true,否则返回false
>>> callable(getattr(a,'sort'))
True
下面这段代码列出对象所有函数:
methodList = [method for method in dir(object) if callable(getattr(object,method))]
globals()和locals()返回的是全局和局部的名字空间。是map,名字:对象。
isinstance(object, classinfo) 检查object是不是classinfo中列举出的类型,返回布尔值
>>> isinstance(a,list)
True
>>> type(a)
<type 'list'>
>>> type(a) == list
True
issubclass判断类参数class是否是类型参数classinfo的子类
class Line:
pass
class RedLine(Line):
pass
class Rect:
pass
print(issubclass(RedLine, Line))
print(issubclass(Rect, Line))
inspect模块提供了一系列函数用于帮助使用自省。下面仅列出较常用的一些函数
检查对象类型
is{module|class|function|method|builtin}(obj):
检查对象是否为模块、类、函数、方法、内建函数或方法。
isroutine(obj):
用于检查对象是否为函数、方法、内建函数或方法等等可调用类型。用这个方法会比多个is*()更方便,不过它的实现仍然是用了多个is*()。
接口与抽象类
接口相当于定义了一个类的样子(所拥有的属性和方法)
抽象类(包含一个或多个抽象方法的类),不能被直接实例化,必须被继承,也就是说它要求子类必须实现某些方法。
从某种意义上来说,他们具有相似的含义。抽象类可以当做接口使用。
由于python 没有抽象类和借口的概念,所以要实现这种功能得abc.py 这个类库
from abc import ABCMeta, abstractmethod
class A:
__metaclass__ = ABCMeta
def __init__(self):
pass
@abstractmethod
def echo(self):
pass
class B(A):
def echo(self,a):
print a
obj = B()
obj.echo(‘abc')
可以看出python 抽象类比较简陋,要求不严格,连抽象方法的个数也不要求。
PHP的面向对象
关键元素(成员方法 成员变量 类常量)
<?php
class a{
const CON="wangwei";
public $var = a;
public function func(){
echo "in func a";
}
}
成员方法
成员变量
类常量
类成员是不是静态的(静态属性,不经过实例化就可以用),即python里的类变量(方法外,看位置)和类方法(classmethod修饰),只看有没有static修饰。static代表的静态意味着整个进程的持续存储的能力。包括局部静态变量。
使用static修饰的成员变量相当于python的类变量。
class abc{
public function func(){
static $b = 0;
$b += 1;
echo $b;
}
public static function func2(){
}
}
$obj = new abc();
$obj2 = new abc();
$obj->func();
$obj->func();
$obj->func();
$obj2->func();
$obj2->func();
$obj2->func();
123456
实例化与元素引用
$obj = new abc();
引用源5个:类名 self 实例名 $this parent
前两者和最后一个,只能用来引用静态成员。
在类的内部 self表示类本身。$this表示实例本身。parent表示父类和父类对象(使用parent::引用父类的所有方法,无论是否静态)。
引用符有 ->和::两种,分别用来引用普通成员和静态成员。
php的成员方法没有符号绑定,只能做硬性限制。即静态方法中不可以使用$this引用普通成员。这比较好理解。静态方法不经实例化就可以使用,而$this则是代表实例。
成员变量的定义与引用
普通成员变量(实例变量)
可以在所有方法的外部定义(使用这种),在普通成员方法中定义,甚至可以在类外部定义
可以在类的内部使用$this->引用,在类的外部使用实例->引用。
<?php
class abc{
public $var = 1;
public function func(){
echo $this->var;
$this->var2 = 2;
}
public function func2(){
echo $this->var2;
}
}
$obj = new abc();
$obj->func();
$obj->func2();
echo $obj->var2;
静态成员变量(类变量)
只可以在所有方法的外部定义
在类的类的内部使用self:: $this:: abc:: 引用。在外部使用实例名:: 和类名::引用
<?php
class abc{
public static $var = 1;
public function func(){
echo self::$var;
echo $this::$var;
echo abc::$var;
}
}
$obj = new abc();
$obj->func();
echo $obj::$var;
echo abc::$var;
静态成员变量的改变会影响这个类的所有实例。
class abc{
public $var = 1;
public static $var2 = 2;
public function func(){
$this->var = 3;
self::$var2 = 4;
}
}
$obj = new abc();
$obj2 = new abc();
$obj->func();
echo $obj->var;
echo $obj::$var2;
echo $obj2->var;
echo $obj2::$var2;
class abc{
public static $b = 0;
public function func(){
self::$b += 1;
echo self::$b;
}
public static function func2(){
}
}
$obj = new abc();
$obj2 = new abc();
$obj->func();
$obj2->func();
成员方法的引用
普通成员方法(实例方法)
可以在类的内部使用$this->引用,在类的外部使用实例->引用。
class abc{
public $var = 1;
public function func(){
print $this->var;
}
public function func2(){
print $this->var;
$this->func();
}
}
$obj = new abc();
$obj->func2();
静态成员方法(类方法)
在类的内部使用self:: abc:: 引用。在外部使用实例名:: 和类名::引用
class abc{
public static $var = 1;
public static function func(){
echo self::$var;
}
public static function func2(){
self::func();
abc::func();
}
}
$obj = new abc();
$obj::func2();
abc::func2();
类常量的定义与访问(值不可改变)
在类的内部使用self:: abc:: 引用。在外部使用实例名:: 和类名::引用
class abc{
const CVAR = 9;
public function func(){
echo self::CVAR;
echo abc::CVAR;
}
}
$obj = new abc();
$obj->func();
echo $obj::CVAR;
echo abc::CVAR;
访问控制
面向对象的一大特性是封装性,即数据隐藏。数据隐藏的对象是:成员变量、成员方法。而类常量是不可隐藏的。隐藏的方法是加关键字:
Public,private,protected
Public:这是默认的类型。公开的。可以随意调用。子类可以集成父类所有的公共成员。
Private:只能在类内,不能在类外调用(即实例化后或者调用静态),也不可被子类继承。
Protected:只能在类内和子类内部调用,是私有数据的折中方案。
class abc{
public function func(){
echo "public func ";
}
private static function func2(){
//私有或者受保护类型定义成静态没有意思 因为外部不可访问
echo "private func ";
}
protected function func3(){
echo "protected func ";
}
public function use_func(){
$this->func();
self::func2();
$this->func3();
}
}
$obj = new abc();
$obj->use_func();
$obj->func();
//PHP Fatal error: Call to private method abc::func2() from context
//$obj->func2();
//PHP Fatal error: Call to protected method abc::func3()
//$obj->func3();
class cd extends abc{
public function sub_func(){
$this->func();
//PHP Fatal error: Call to private method abc::func2() from context
//$this->func2();
$this->func3();
}
}
$obj = new cd();
$obj->sub_func();
$obj->func();
构造与析构
惨绝人寰,如果在子类中不显示的调用父类的构造函数,则不会自动执行父类的。(也难怪,没有理由要求,参数同步)
如果子类没有构造方法,则父类的构造方法会自动执行
class abc{
public function __construct($name,$age){
$this->name = $name;
$this->age = $age;
}
}
class cd extends abc{
}
$obj = new cd('wangwei',25);
echo $obj->name,$obj->age;
如果有的话,则需要显式调用,parent只代表自己的直属父类
class origin {
public function __construct($name,$age){
$this->name_or = $name;
$this->age_or = $age;
}
}
class abc extends origin{
public function __construct($name,$age){
$this->name = $name;
$this->age = $age;
parent::__construct($name,13);
}
}
class cd extends abc{
public function __construct($name){
$this->sub_name = $name;
parent::__construct($name,12);
}
}
$obj = new cd('wangwei');
echo $obj->name,$obj->age;
echo $obj->name_or,$obj->age_or;
继承、多态性(覆盖与重载)
在大多数人看来,多继承并不是一种好的设计方法。php只支持单继承。
无论是调用子类的某个方法、还是在父类中调用某一个方法,都是子类同名(同性、即要么非静态要么静态否则报错)方法覆盖父类的。可以使用parent::直接调用父类。
class abc {
public function func(){
echo 'parent';
}
public function use_func(){
$this->func();
}
}
class cd extends abc{
public function func(){
parent::func();
echo 'subclass';
}
}
$obj = new cd();
$obj->use_func();
parentsubclass
使用__call魔术方法来实现重载。
class A{
public function __call($name, $num){
var_dump($name);
var_dump(count($num));
$func_name = $name.count($num);
$this->$func_name();
}
public function func0(){
echo 'haha';
}
}
$obj = new A();
$obj->func();
自省与反射
反射api是php内建的oop技术扩展,包括一些类,异常和接口,综合使用他们可用来帮助我们分析其它类,接口,方法,属性,方法和扩展。这些oop扩展被称为反射。
通过ReflectionClass,我们可以得到类的以下信息:
1)常量 Contants
2)属性 Property Names
3)方法 Method Names
5)命名空间 Namespace
6)Person类是否为final或者abstract
/article/1208363.html
http://php.net/manual/en/book.reflection.php
/article/8174559.html
接口与抽象类
PHP 5 支持抽象类和抽象方法。定义为抽象的类不能被实例化。任何一个类,如果它里面至少有一个方法是被声明为抽象的,那么这个类就必须被声明为抽象的。被定义为抽象的方法只是声明了其调用方式(参数),不能定义其具体的功能实现。
继承一个抽象类的时候,子类必须定义父类中的所有抽象方法;另外,这些方法的访问控制必须和父类中一样(或者更为宽松)。
abstract class AbstractClass
{
// 强制要求子类定义这些方法
abstract protected function getValue();
abstract protected function prefixValue($prefix);
// 普通方法(非抽象方法)
public function printOut() {
print $this->getValue() . "\n";
}
}
class ConcreteClass1 extends AbstractClass
{
protected function getValue() {
return "ConcreteClass1";
}
public function prefixValue($prefix) {
return "{$prefix}ConcreteClass1";
}
}
使用接口(interface),你可以指定某个类必须实现哪些方法,但不需要定义这些方法的具体内容。我们可以通过interface来定义一个接口,就像定义一个标准的类一样,但其中定义所有的方法都是空的。接口中定义的所有方法都必须是public,这是接口的特性。
实现
要实现一个接口,可以使用implements操作符。类中必须实现接口中定义的所有方法,否则 会报一个fatal错误。如果要实现多个接口,可以用逗号来分隔多个接口的名称。
Note:
实现多个接口时,接口中的方法不能有重名。
Note:
接口也可以继承,通过使用extends操作符。
常量
接口中也可以定义常量。接口常量和类常量的使用完全相同。 它们都是定值,不能被子类或子接口修改。
/ 声明一个'iTemplate'接口
interface iTemplate
{
const CONST_VAR = 'CONTST';
public function setVariable($name, $var);
public function getHtml($template);
}
// 实现接口
class Template implements iTemplate
{
private $vars = array();
public function setVariable($name, $var)
{
$this->vars[$name] = $var;
}
public function getHtml($template)
{
return $this->vars;
}
}
$obj = new Template();
$obj->setVariable('name','wangwei');
var_dump($obj->getHtml(''));
echo $obj::CONST_VAR;
相关文章推荐
- ipython notebook [jupyter] 使用
- 4.2.4 Python特有编码
- 4.2.4 Python特有编码
- Python学习笔记005_文件_OS_模块_pickle
- python random从集合中随机选择元素
- Head Frist Python 读书笔记 构建发布
- mac python install module
- python文件读写总结
- python 学习笔记
- python实现mysql的单引号字符串过滤方法
- python modules: os, sys, platform
- python发送邮件
- Python4周 入职培训的过程
- 学习Python:做数据科学还是网站开发?
- Python核心数据类型——字符串(2)
- Python如何自定义异常
- Python一行核心代码实现杨辉三角
- 使用matplot绘图 @python
- Python 实现二分查找算法
- 第二篇python