程序执行需要读取到安全敏感项必需在androidmanifest.xml中声明相关权限请求, 完整列表如下:

  • android.permission.ACCESS_CHECKIN_PROPERTIES 允许读写访问”properties”表在 checkin数据库中,改值可以修改上传( Allows read/write access to the “properties” table in the checkin database, to change values that get uploaded)

  • android.permission.ACCESS_COARSE_LOCATION 允许一个程序访问CellID或WiFi热点来获取粗略的位置(Allows an application to access coarse (e.g., Cell-ID, WiFi) location)

  • android.permission.ACCESS_FINE_LOCATION 允许一个程序访问精良位置(如GPS) (Allows an application to access fine (e.g., GPS) location)

  • android.permission.ACCESS_LOCATION_EXTRA_COMMANDS 允许应用程序访问额外的位置提供命令(Allows an application to access extra location provider commands)

  • android.permission.ACCESS_MOCK_LOCATION 允许程序创建模拟位置提供用于测试(Allows an application to create mock location providers for testing)

  • android.permission.ACCESS_NETWORK_STATE 允许程序访问有关GSM网络信息(Allows applications to access information about networks)

  • android.permission.ACCESS_SURFACE_FLINGER 允许程序使用SurfaceFlinger底层特性 (Allows an application to use SurfaceFlinger’s low level features)

  • android.permission.ACCESS_WIFI_STATE 允许程序访问Wi-Fi网络状态信息(Allows applications to access information about Wi-Fi networks)

  • android.permission.ADD_SYSTEM_SERVICE 允许程序发布系统级服务(Allows an application to publish system-level services).

  • android.permission.BATTERY_STATS 允许程序更新手机电池统计信息(Allows an application to update the collected battery statistics)

  • android.permission.BLUETOOTH 允许程序连接到已配对的蓝牙设备(Allows applications to connect to paired bluetooth devices)

  • android.permission.BLUETOOTH_ADMIN 允许程序发现和配对蓝牙设备(Allows applications to discover and pair bluetooth devices)

  • android.permission.BRICK 请求能够禁用设备(非常危险)(Required to be able to disable the device (very *erous!).)

  • android.permission.BROADCAST_PACKAGE_REMOVED 允许程序广播一个提示消息在一个应用程序包已经移除后(Allows an application to broadcast a notification that an application package has been removed)

  • android.permission.BROADCAST_STICKY 允许一个程序广播常用intents(Allows an application to broadcast sticky intents)

  • android.permission.CALL_PHONE 允许一个程序初始化一个电话拨号不需通过拨号用户界面需要用户确认 (Allows an application to initiate a phone call without going through the Dialer user interface for the user to confirm the call being placed.)

  • android.permission.CALL_PRIVILEGED 允许一个程序拨打任何号码,包含紧急号码无需通过拨号用户界面需要用户确认 (Allows an application to call any phone number, including emergency numbers, without going through the Dialer user interface for the user to confirm the call being placed)

  • android.permission.CAMERA 请求访问使用照相设备(Required to be able to access the camera device. )

  • android.permission.CHANGE_COMPONENT_ENABLED_STATE 允许一个程序是否改变一个组件或其他的启用或禁用(Allows an application to change whether an application component (other than its own) is enabled or not. )

  • android.permission.CHANGE_CONFIGURATION 允许一个程序修改当前设置,如本地化(Allows an application to modify the current configuration, such as locale. )

  • android.permission.CHANGE_NETWORK_STATE 允许程序改变网络连接状态(Allows applications to change network connectivity state)

  • android.permission.CHANGE_WIFI_STATE 允许程序改变Wi-Fi连接状态(Allows applications to change Wi-Fi connectivity state)

  • android.permission.CLEAR_APP_CACHE 允许一个程序清楚缓存从所有安装的程序在设备中(Allows an application to clear the caches of all installed applications on the device. )

  • android.permission.CLEAR_APP_USER_DATA 允许一个程序清除用户设置(Allows an application to clear user data)

  • android.permission.CONTROL_LOCATION_UPDATES 允许启用禁止位置更新提示从无线模块 (Allows enabling/disabling location update notifications from the radio. )

  • android.permission.DELETE_CACHE_FILES 允许程序删除缓存文件(Allows an application to delete cache files)

  • android.permission.DELETE_PACKAGES 允许一个程序删除包(Allows an application to delete packages)

  • android.permission.DEVICE_POWER 允许访问底层电源管理(Allows low-level access to power management)

  • android.permission.DIAGNOSTIC 允许程序RW诊断资源(Allows applications to RW to diagnostic resources. )

  • android.permission.DISABLE_KEYGUARD 允许程序禁用键盘锁(Allows applications to disable the keyguard )

  • android.permission.DUMP 允许程序返回状态抓取信息从系统服务(Allows an application to retrieve state dump information from system services.)

  • android.permission.EXPAND_STATUS_BAR 允许一个程序扩展收缩在状态栏,android开发网提示应该是一个类似Windows Mobile中的托盘程序(Allows an application to expand or collapse the status bar. )

  • android.permission.FACTORY_TEST 作为一个工厂测试程序,运行在root用户(Run as a manufacturer test application, running as the root user. )

  • android.permission.FLASHLIGHT 访问闪光灯,android开发网提示HTC Dream不包含闪光灯(Allows access to the flashlight )

  • android.permission.FORCE_BACK 允许程序强行一个后退操作是否在顶层activities(Allows an application to force a BACK operation on whatever is the top activity. )

  • android.permission.FOTA_UPDATE 暂时不了解这是做什么使用的,android开发网分析可能是一个预留权限.

  • android.permission.GET_ACCOUNTS 访问一个帐户列表在Accounts Service中(Allows access to the list of accounts in the Accounts Service)

  • android.permission.GET_PACKAGE_SIZE 允许一个程序获取任何package占用空间容量(Allows an application to find out the space used by any package. )

  • android.permission.GET_TASKS 允许一个程序获取信息有关当前或最近运行的任务,一个缩略的任务状态,是否活动等等(Allows an application to get information about the currently or recently running tasks: a thumbnail representation of the tasks, what activities are running in it, etc.)

  • android.permission.HARDWARE_TEST 允许访问硬件(Allows access to hardware peripherals. )

  • android.permission.INJECT_EVENTS 允许一个程序截获用户事件如按键、触摸、轨迹球等等到一个时间流,android 开发网提醒算是hook技术吧(Allows an application to inject user events (keys, touch, trackball) into the event stream and deliver them to ANY window.)

  • android.permission.INSTALL_PACKAGES 允许一个程序安装packages(Allows an application to install packages. )

  • android.permission.INTERNAL_SYSTEM_WINDOW 允许打开窗口使用系统用户界面(Allows an application to open windows that are for use by parts of the system user interface. )

  • android.permission.INTERNET 允许程序打开网络套接字(Allows applications to open network sockets)

  • android.permission.MANAGE_APP_TOKENS 允许程序管理(创建、催后、 z- order默认向z轴推移)程序引用在窗口管理器中(Allows an application to manage (create, destroy, Z-order) application tokens in the window manager. )

  • android.permission.MASTER_CLEAR 目前还没有明确的解释,android开发网分析可能是清除一切数据,类似硬格机

  • android.permission.MODIFY_AUDIO_SETTINGS 允许程序修改全局音频设置(Allows an application to modify global audio settings)

  • android.permission.MODIFY_PHONE_STATE 允许修改话机状态,如电源,人机接口等(Allows modification of the telephony state ? power on, mmi, etc. )

  • android.permission.MOUNT_UNMOUNT_FILESYSTEMS 允许挂载和反挂载文件系统可移动存储 (Allows mounting and unmounting file systems for removable storage. )

  • android.permission.PERSISTENT_ACTIVITY 允许一个程序设置他的activities显示 (Allow an application to make its activities persistent. )

  • android.permission.PROCESS_OUTGOING_CALLS 允许程序监视、修改有关播出电话(Allows an application to monitor, modify, or abort outgoing calls)

  • android.permission.READ_CALENDAR 允许程序读取用户日历数据(Allows an application to read the user’s calendar data.)

  • android.permission.READ_CONTACTS 允许程序读取用户联系人数据(Allows an application to read the user’s contacts data.)

  • android.permission.READ_FRAME_BUFFER 允许程序屏幕波或和更多常规的访问帧缓冲数据(Allows an application to take screen shots and more generally get access to the frame buffer data)

  • android.permission.READ_INPUT_STATE 允许程序返回当前按键状态(Allows an application to retrieve the current state of keys and switches. )

  • android.permission.READ_LOGS 允许程序读取底层系统日志文件(Allows an application to read the low-level system log files. )

  • android.permission.READ_OWNER_DATA 允许程序读取所有者数据(Allows an application to read the owner’s data)

  • android.permission.READ_SMS 允许程序读取短信息(Allows an application to read SMS messages.)

  • android.permission.READ_SYNC_SETTINGS 允许程序读取同步设置(Allows applications to read the sync settings)

  • android.permission.READ_SYNC_STATS 允许程序读取同步状态(Allows applications to read the sync stats)

  • android.permission.REBOOT 请求能够重新启动设备(Required to be able to reboot the device. )

  • android.permission.RECEIVE_BOOT_COMPLETED 允许一个程序接收到 ACTION_BOOT_COMPLETED广播在系统完成启动(Allows an application to receive the ACTION_BOOT_COMPLETED that is broadcast after the system finishes booting. )

  • android.permission.RECEIVE_MMS 允许一个程序监控将收到MMS彩信,记录或处理(Allows an application to monitor incoming MMS messages, to record or perform processing on them. )

  • android.permission.RECEIVE_SMS 允许程序监控一个将收到短信息,记录或处理(Allows an application to monitor incoming SMS messages, to record or perform processing on them.)

  • android.permission.RECEIVE_WAP_PUSH 允许程序监控将收到WAP PUSH信息(Allows an application to monitor incoming WAP push messages. )

  • android.permission.RECORD_AUDIO 允许程序录制音频(Allows an application to record audio)

  • android.permission.REORDER_TASKS 允许程序改变Z轴排列任务(Allows an application to change the Z-order of tasks)

  • android.permission.RESTART_PACKAGES 允许程序重新启动其他程序(Allows an application to restart other applications)

  • android.permission.SEND_SMS 允许程序发送SMS短信(Allows an application to send SMS messages)

  • android.permission.SET_ACTIVITY_WATCHER 允许程序监控或控制activities已经启动全局系统中Allows an application to watch and control how activities are started globally in the system.

  • android.permission.SET_ALWAYS_FINISH 允许程序控制是否活动间接完成在处于后台时Allows an application to control whether activities are immediately finished when put in the background.

  • android.permission.SET_ANIMATION_SCALE 修改全局信息比例(Modify the global animation scaling factor.)

  • android.permission.SET_DEBUG_APP 配置一个程序用于调试(Configure an application for debugging.)

  • android.permission.SET_ORIENTATION 允许底层访问设置屏幕方向和实际旋转(Allows low-level access to setting the orientation(actually rotation) of the screen.)

  • android.permission.SET_PREFERRED_APPLICATIONS 允许一个程序修改列表参数 PackageManager.addPackageToPreferred() 和PackageManager.removePackageFromPreferred()方法(Allows an application to modify the list of preferred applications with the PackageManager.addPackageToPreferred() and PackageManager.removePackageFromPreferred() methods.)

  • android.permission.SET_PROCESS_FOREGROUND 允许程序当前运行程序强行到前台(Allows an application to force any currently running process to be in the foreground.)

  • android.permission.SET_PROCESS_LIMIT 允许设置最大的运行进程数量(Allows an application to set the maximum number of (not needed) application processes that can be running. )

  • android.permission.SET_TIME_ZONE 允许程序设置时间区域(Allows applications to set the system time zone)

  • android.permission.SET_WALLPAPER 允许程序设置壁纸(Allows applications to set the wallpaper )

  • android.permission.SET_WALLPAPER_HINTS 允许程序设置壁纸hits(Allows applications to set the wallpaper hints)

  • android.permission.SIGNAL_PERSISTENT_PROCESSES 允许程序请求发送信号到所有显示的进程中 (Allow an application to request that a signal be sent to all persistent processes)

  • android.permission.STATUS_BAR 允许程序打开、关闭或禁用状态栏及图标Allows an application to open, close, or disable the status bar and its icons.

  • android.permission.SUBSCRIBED_FEEDS_READ 允许一个程序访问订阅RSS Feed内容提供(Allows an application to allow access the subscribed feeds ContentProvider. )

  • android.permission.SUBSCRIBED_FEEDS_WRITE 系统暂时保留改设置,android开发网认为未来版本会加入该功能。

  • android.permission.SYSTEM_ALERT_WINDOW 允许一个程序打开窗口使用 TYPE_SYSTEM_ALERT,显示在其他所有程序的顶层(Allows an application to open windows using the type TYPE_SYSTEM_ALERT, shown on top of all other applications. )

  • android.permission.VIBRATE 允许访问振动设备(Allows access to the vibrator)

  • android.permission.WAKE_LOCK 允许使用PowerManager的 WakeLocks保持进程在休眠时从屏幕消失( Allows using PowerManager WakeLocks to keep processor from sleeping or screen from dimming)

  • android.permission.WRITE_APN_SETTINGS 允许程序写入API设置(Allows applications to write the apn settings)

  • android.permission.WRITE_CALENDAR 允许一个程序写入但不读取用户日历数据(Allows an application to write (but not read) the user’s calendar data. )

  • android.permission.WRITE_CONTACTS 允许程序写入但不读取用户联系人数据(Allows an application to write (but not read) the user’s contacts data. )

  • android.permission.WRITE_GSERVICES 允许程序修改Google服务地图(Allows an application to modify the Google service map. )

  • android.permission.WRITE_OWNER_DATA 允许一个程序写入但不读取所有者数据(Allows an application to write (but not read) the owner’s data.)

  • android.permission.WRITE_SETTINGS 允许程序读取或写入系统设置(Allows an application to read or write the system settings. )

  • android.permission.WRITE_SMS 允许程序写短信(Allows an application to write SMS messages)

  • android.permission.WRITE_SYNC_SETTINGS 允许程序写入同步设置(Allows applications to write the sync settings)

- void onCreate():当Service第一次被创建后将立即回调该方法。
- void onStartCommand(Intent intent,int flag,int startId):每次通过startService()方法启动Service时都会被回调
- void onDestroy():当Service被关闭前会被回调
- abstract IBinder onBind(Intent intent):该方法是Service子类必须实现的方法,如果不需要通过绑定的方式启动服务,可以返回Null
- boolean onUnbind(Intent intent):当Service上绑定的所有客户端都被断开连接将被回调该方法

通过Service的启动方式与适用范围,可将Service分为两类服务:
- start:启动服务,当一个Android组件(如一个Activity)调用startService()的时候,启动一个Service。Service一旦启动,就可以一直在后台运行下去,即使这个启动它的组件被摧毁。这样的服务模式,通常用于执行一个操作而不需要返回结果给调用者。
- Bound:绑定服务,当一个Android组件(如一个Activity)调用binService(),一个绑定Service提供了一个客户端到服务端的接口,允许组件与服务之间进行交互,这样可以实现跨进程的通信。绑定服务的生命周期默认是跟随他的绑定组件的,但是一个绑定Service可以绑定多个Android组件,如果这些Android组件都被销毁,那么这个绑定服务也将被销毁。

一个Service可以包含上面两种运行方式,与它重载的方法有关,如果重写了onStartCommand()即支持启动Service,如果重写onBind()即支持绑定服务,如果重载实现了两个方法即可实现两种服务。

对于两种方式的Service,其生命周期被回调的方法也是不一样的:

清单文件的配置

Service是Android的四大组件之一,所以它必须在AndroidManifest清单文件中进行配置,否则系统将找不到这个服务。Service的配置也是在<application />这个节点下,使用<service />进行配置,其中android:name属性为Service类。
如果开发的Service需要被外部操作,还需要配置<intent-filter />节点。
如果Service强制仅本应用操作,可以配置<service />节点的android:exported属性为false,这样即使配置了<intent-filter />,外部应用也无法操作这个服务,android:exported属性默认是true。

Service开发步骤

  1. 开发一个Service类,需要继承Service或者IntentService
  2. 在AndroidManifest清单文件中注册这个Service组件
  3. 在一个Android组件中启动这个Service
  4. Service使用完成之后,需要停止这个服务

启动Service

启动服务必须实现Service.onStartCommond()方法,启动服务使用startService(Intent intent)方法开启一个服务,仅需要传递一个Intent对象即可,在Intent对象中指定需要启动的服务,而使用startService()方法启动的服务,在服务的外部,必须使用stopService()方法停止当前的Service,该Service就会被销毁。
对于启动服务,一旦启动将与访问它的组件无任何关联,即使访问它的组件被销毁了,这个服务也一直运行下去,直到被销毁!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class StartService extends Service{
private final static String TAG = "StartService";
@Override
public IBinder onBind(Intent arg0) {
// 仅通过startService()启动服务,所以这个方法返回null即可。
return null;
}
@Override
public void onCreate(){
super.onCreate();
Log.i(TAG,"Service is Create");
}
@Override
public void onCreate() {
super.onCreate();
Log.i(TAG, "Service is Created");
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i(TAG, "Service is started");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
Log.i(TAG, "Service is Destroyed");
super.onDestroy();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class MainActivity extends Activity{
private Button startSer,stopSer;
private Intent intent = null;

@Override
protected void onCreate(Bundle saveInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
startSer = (Button) findViewById(R.id.startSer);
stopSer = (Button) findViewById(R.id.stopSer);

intent = new Intent(MainActivity,StartService.class);
startSer.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startService(intent);
}
}
stopSer.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
stopService(intent);
}
}
}
}

绑定服务

如果Service和宿主之间需要进行方法调用或者数据交换,则应该使用Context对象的binService()和unbindSevice()方法来绑定和解除绑定服务。
Context的binService()方法的完整方法签名为:binService(Intent service,ServiceConnection conn,int flags)
- service:通过Intent指定要绑定的Service
- conn:一个ServiceConnection对象,该对象用于监听访问者与Service对象的onServiceConnected()方法
- flags:指定绑定时是否自动创建Service,0为不自动创建,BIND_AUTO_CREATE为自动创建

从上面的bindService方法可以看出,绑定一个服务与宿主交互,依托一个ServiceConnection接口,这个接口对象必须声明在主线程中,通过实现其中的两个方法,来实现与Service的交互。
- void onServiceConnection(ComponentName name,IBinder service):绑定服务的时候被回调,在这个方法获取绑定Service传递过来的IBinder对象,通过这个IBinder对象,实现宿主和Service的交互。
- void onServiceDisconnected(ComponetName name):当取消绑定时回调,但正常情况下不被调用,它的调用时机是当Service服务被意外销毁时,才被自动调用。

在绑定Service时,必须提供一个IBinder onBind(Intent intent)方法,在绑定本地Service的情况下,onBind()方法h返回的IBinder对象会传给宿主ServiceConnection.onServiceConnected()方法发service参数,这样宿主就可用通过IBinder对象与Service进行通信。实际开发中一般会继承Binder类(IBinder的实现类)的方式来实现自己的IBinder对象。

如果绑定服务提供的onBind返回为Null,则也可以使用bindService()启动服务,但不会绑定上Service,因此宿主的ServiceConnection.onServiceConnected()方法不会被执行。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
// MainActivity.java
public class MainActivity extends Activity {
private final String TAG = "MainActivity";
private Button bn1,bn2,bn3;
private MyService.MyBinder binder;
private ServiceConnection conn = new ServiceConnection() {

@Override
public void onServiceDisconnected(ComponentName name) {
Log.d(TAG, "--Service Disconnected--");
}

@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Toast.makeText(MainActivity.this, "绑定成功", Toast.LENGTH_SHORT).show();
binder = (MyService.MyBinder) service;
}
};

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final Intent intent = new Intent(MainActivity.this,MyService.class);

bn1 = (Button) findViewById(R.id.button1);
bn1.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
bindService(intent, conn, Service.BIND_AUTO_CREATE);
}
});
bn2 = (Button) findViewById(R.id.button2);
bn2.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
binder = null;
unbindService(conn);
}
});
bn3 = (Button) findViewById(R.id.button3);
bn3.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if(binder!=null){
Toast.makeText(MainActivity.this, "Count"+binder.getCount(), Toast.LENGTH_SHORT).show();
}else {
Toast.makeText(MainActivity.this, "未绑定", Toast.LENGTH_SHORT).show();
}
}
});

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<!-- activity_main.xml -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.example.service.MainActivity" >

<Button
android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="bind" />

<Button
android:id="@+id/button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="unbind" />

<Button
android:id="@+id/button3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="getCount" />

</LinearLayout>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
//MyService.java
public class MyService extends Service {
private final static String TAG = "MyService";
private int count;
private boolean quit = true;

private Thread thread;
private MyBinder binder = new MyBinder();

public class MyBinder extends Binder {
public int getCount() {
return count;
}
}

@Override
public void onCreate() {
super.onCreate();
thread = new Thread(new Runnable() {
@Override
public void run() {
while (quit) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {

}
count++;
}
}
});
thread.start();
}

@Override
public IBinder onBind(Intent intent) {
return binder;
}

}

面向对象编程————Object Oriented Programming,简称OOP,是一种程序设计思想。OOP吧对象作为程序的基本单元,一个对象包含了数据和操作数据的函数。

面向过程的程序设计把计算机程序视为一系列的命令集合,即一组函数的顺序执行,为了简化程序设计,面向过程吧函数继续切分成为子函数,即把大块的函数通过切割成小块函数降低系统的复杂度。

而面向对象的程序设计吧计算机程序视为一组对象的集合,而每个对象都可以接受其他对象发过来的消息,并处理这些消息,计算机程序的执行就是一系列消息在各个对象之间传递。

在Python中,所有数据类型都可以视为对象,当然也可以自定义对象,自定义的对象数据类型就是面向对象中的类(Class)的概念。

类和实例

面向对象最重要的概念就是类(Class)和实例(Instance),必须牢记类是抽象的模板,而实例是根据类创建出来的一个个具体的‘对象’,每个对象都拥有相同的方法,但各自的数据可能不同。
以Student类为例,在Python中,定义类是通过class关键字

1
2
class Student(object):
pass

clas后米娜紧接着是类名,即’Student’,类名通过是大写开头的单词,紧接着是(object),表示类是从哪个类继承下来的,object是所有类都会继承的类。
定义了好了Student类,就可以根据Student类创建出Student的实例,创建实例是通过类名+()实现的:

1
2
3
4
5
6
7
8
9
10
11
12
13
>>> jack = Student()
>>> jack
<__main__.Student object at 0x10f5e3110>
>>> Student
<class '__main__.Student'>
>>>
通过一个特殊的`__init__`方法,在创建实例的时候,就可以对某些属性初始化
```Python
class Student(object):

def __init__(self, name, score):
self.name = name
self.score = score

注意__innit__方法的第一个参数永远是self,表示创建的实例本身,因此在__init__方法内部,就可以把各个属性绑定到self,因为self就指向创建的实例本身。

有了__init__方法,在创建实例的时候,就不能传入空的参数了,必须传入与__init__方法匹配的参数,但self不需要传,Python解释器自己会把实例变量穿进去:

1
2
3
4
5
6
>>> class Student(object):
...
... def __init__(self,name):
... self.name = name
...
>>> jack = Student('jack')

数据封装

面向对象编程的一个重要特点就是数据封装。
类本身就拥有数据,要访问数据,就没有必要从外面的函数去访问,就直接用类内部定义访问数据的函数,这样就把“数据”给封装起来了,这些封装数据的函数是和类本身关联起来的,外面称之为类的方法,要定义一个方法,除了第一个参数是self外,其他和普通函数一样。要调用一个方法,只需要在实例变量上直接调用,出了self不用传递,其他参数正常传入。

1
2
3
4
5
6
7
8
9
>>> class Student(object):
... def __init__(self,name):
... self.name = name
... def getName(self):
... return self.name
...
>>> jack = Student('jack')
>>> jack.getName()
'jack'

访问限制

在Class内部,可以有属性和方法,而外部代码可以通过直接调用实例变量的方法来操作数据,这样就可以隐藏内部的复杂逻辑。
为了内部属性不被外部访问,可以把属性的名称前加上两个下划线__,在Python中,实例变量名如果以__开头,就变成了一个私有变量(private),只有内部可以访问,外部不能访问。
需要注意的是,在Python中变量名类似__xxx__的,也就是以双下划线开头,并且以双下划线结尾的,是特殊变量,特殊变量是可以直接访问的,不是private变量,所以不能用__name__这样的变量名。
双下划线开头的实例变量外部不能直接访问是因为Python解释器对外把__name变量改成了_Student__name,所以,仍然可以通过_Student__name来访问__name变量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#  /usr/bin/env python
# -*- coding:utf-8 -*-

class Student(object):
def __init__(self,name):
self.__name = name
def print_name(self):
print '%s' % (self.__name)

jack = Student('jack')
jack.print_name()
print jack._Student__name
# 执行结果
# jack
# jack

但不同版本的Python解释器可能会把__name改成不同的变量名。

继承和多态

在OOP程序设计中,定义一个class的时候,可以从某个现有的class继承,新的class称为子类(Subclass),而被继承的class称为基类、父类或者超类(Base class、Super class)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Animal(object):
def run(self):
print 'Animal is running...'

class Dog(Animal):
def run(self):
print 'Dog is running...'

class Cat(Animal):
pass

dog = Dog()
dog.run()

cat = Cat()
cat.run()
# 运行结果
# Dog is running...
# Animal is running...

判断一个变量是否是某个类型可以用isinstance()判断:

1
2
3
4
5
6
7
8
9
10
11
12
13
>>> a = list() #  a是list类型
>>> b = Animal() # b是Animal类型
>>> c = Dog() # c是Dog类型
>>> isinstance(a,list)
True
>>> isinstance(b,list)
False
>>> isinstance(b,Animal)
True
>>> isinstance(c,Animal)
True
>>> isinstance(c,Dog)
True

获取对象信息

使用type()

判断对象类型,使用type()函数

1
2
3
4
5
6
7
8
>>> type(123)
<type 'int'>
>>> type('str')
<type 'str'>
>>> type(None)
<type 'NoneType'>
>>> type(abs)
<type 'builtin_function_or_method'>

Python把每种type类型都定义好了常量,放在types模块里,使用之前,需要先导入:

1
2
3
4
5
6
7
8
9
>>> import types
>>> type('abc')==types.StringType
True
>>> type(u'abc')==types.UnicodeType
True
>>> type([])==types.ListType
True
>>> type(str)==types.TypeType
True

使用isinstance()

对于class的继承关系,可以用isinstance()函数判断

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
>>> class Animal(object):
... pass
...
>>> class Dog(Animal):
... pass
...
>>> class Husky(Dog):
... pass
...
>>> a = Animal()
>>> d = Dog()
>>> h = Husky()
>>> isinstance(h,Husky)
True
>>> isinstance(h,Dog)
True
>>> isinstance(h,Animal)
True
>>> isinstance(d,Husky)
False
>>> isinstance(d,Dog)
True
>>> isinstance(d,Animal)
True

>>> isinstance(d,(Animal,Husky))# d是否为Animal,Husky中的一种
True

使用dir()

获取一个对象的所有属性和方法,可以使用dir()函数,它返回一个包含字符串的list

1
2
>>> dir('ABC')
['__add__', '__class__', '__contains__', '__delattr__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__getslice__', '__gt__', '__hash__', '__init__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '_formatter_field_name_split', '_formatter_parser', 'capitalize', 'center', 'count', 'decode', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'index', 'isalnum', 'isalpha', 'isdigit', 'islower', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']

类似__xxx__的属性和方法在Python中都有特殊的用途,比如上例的__len__方法返回长度。调用len()函数获取对象长度时,实际回去调用对象的__len__方法。
利用getattr()setattr()hasattr()函数还可以直接操作一个对象的状态:

1
2
3
4
5
6
7
8
9
10
>>> obj = MyObject()
>>> hasattr(obj,'x')# 是否有属性'x'?
True
>>> hasattr(obj,'y')# 是否有属性'y'?
False
>>> setattr(obj,'y',19)# 设置一个属性'y'
>>> hasattr(obj,'y')# 是否有属性'y'?
True
>>> getattr(obj,'y')# 或属性属性'y'
19

可以传入一个default参数,如果属性不存在,就返回默认值:

1
2
>>> getattr(obj, 'z', 404) #  获取属性'z',如果不存在,返回默认值404
404

动态绑定属性和方法

正常情况下,当我们定义了一个class,差创建一个class的实例后,我们可以给该实例绑定任何属性和方法,这就是动态语言的灵活性:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
>>> class Student(object):
... pass
...
>>> s = Student()
>>> s.name = 'Zoe'
>>> print s.name
Zoe
>>> def set_age(self,age): # 定义一个函数作为实例的方法
... self.age = age
...
>>> from types import MethodType
>>> s.set_age = MethodType(set_age,s,Student) # 给实例绑定一个方法
>>> s.set_age(22)
>>> s.age
22

但是其他的实例该方法是不起作用的

1
2
3
4
5
>>> s1 = Student()
>>> s2.set_age(21)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 's2' is not defined

可以给class绑定方法:

Student.set_age = MethodType(set_age,None,Student)
s1.set_age(21)
s1.age
21

s.age
22

1
2
3
4
5
6
7
8
9
10
11
12

## 使用__slots__
如果想要限制class的属性怎么办?比如只允许Student实例添加`name`和`age`属性。
为了达到限制的目的,Python允许在定义class的时候,定义一个特殊的`__slots__`变量,来限制该class能添加的属性:
```Python
>>> s = Student()
>>> s.name = 'Zoe'
>>> s.age = 22
>>> s.score = 99
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Student' object has no attribute 'score'

注意,__slots__定义的属性仅对当前类起作用,对继承的子类是不起作用的

@property

在绑定属性时,我们直接把属性暴露出来,这样还不安全
为了限制属性的使用范围,可以使用@property装饰器把一个方法变成属性调用:

1
2
3
4
5
6
7
8
9
10
11
>>> class Student(object):
... @property
... def score(self):
... return self._score
... @score.setter
... def score(self,value):
... if not isinstance(value,int):
... raise ValueError('score must be an integer!')
... if value < 0 or value > 100:
... raise ValueError('score must between 0 ~ 100')
... self._score = value

@property的实现比较复杂,我们先考察如何使用。把一个getter方法变成属性,只需要加上@property就可以了,此时,@property本身又创建了另一个装饰器@score.setter,负责把一个setter方法变成属性赋值,于是,我们就拥有一个可控的属性操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
>>> s = Student()
>>> s.score = 69
>>> s.score
69
>>> s.score = 101
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 10, in score
ValueError: score must between 0 ~ 100
>>> s.score = '99'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 8, in score
ValueError: score must be an integer!

还可以定义只读属性,只定义getter方法,不定义setter方法就是一个只读属性:

1
2
3
4
5
6
7
8
9
10
11
12
13
class Student(object):

@property
def birth(self):
return self._birth

@birth.setter
def birth(self, value):
self._birth = value

@property
def age(self):
return 2014 - self._birth

上面的birth是可读写属性,而age就是一个只读属性,因为age可以根据birth和当前时间计算出来。

多重继承

Python支持同时继承多个父类

1
class SubClass(SpuClass1,SpuClass2,...)

Mixin

在设计类的继承关系时,通常主线是单一继承下来的。为了混入额外的功能,可以通过多继承实现,这种设计称为Mixing。
为了更好地看出继承关系,可以在添加功能的类后面在Mixin,比如class Dog(Mammal,RunnableMixin,CarnivorousMixin)
Python子弟啊的很多库使用了Mixi,举个例子,Python自带的TCPServerUDPServer这两个网络服务,而要同时多个用户就必须使用多进程或多线程模型,这两种模型由ForkingMixinThreadingMixin提供。

定制类

之前,我们知道了一些形如__xxx__的变量或方法的特殊作用,如:__slots____len__()

Str

类的说明

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
ValueError: score must be an integer!
>>> class Student(object):
... def __init__(self,name):
... self.name = name
...
>>> print Student('Jack')
<__main__.Student object at 0x10e50d910>
>>> class Student(object):
... def __init__(self,name):
... self.name = name
... def __str__(self):
... return 'Student object (name:%s)' % self.name
...
>>> print Student('Jack')
Student object (name:Jack)
>>> s
<__main__.Student object at 0x10e50d790> # 直接输出还是“不好看”
>>> class Student(object):
... def __str__(self):
... return 'Student object (name:%s)' % self.name
... def __init__(self,name):
... self.name = name
... __repr__ = __str__
...
>>> s = Student("Jakc")
>>> s
Student object (name:Jakc)

iter_

如果一个类要呗用于for...in循环,就必须实现一个__iter__()方法,该方法返回一个迭代对象,然后Python的for循环就会不断调用该迭代对象的next()方法拿到循环的下一个值,值得遇到StopIteration错误时退出循环。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
>>> class Fib(object):
... def __init__(self):
... self.a,self.b = 0,1
... def __iter__(self):
... return self
... def next(self):
... self.a,self.b = self.b,self.a + self.b
... if self.a > 100000:
... raise StopIteration()
... return self.a
...
>>> for n in Fib():
... print n
...
1
1
2
3
5
8
13
21
34
55
89
144
233
377
610
987
1597
2584
4181
6765
10946
17711
28657
46368
75025

getitem

要想像list那样按照下标取元素,需要实现__getitem__()方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
>>> class Fib(object):
... def __getitem__(self,n):
... a,b = 1,1
... for x in range(n):
... a,b = b,a+b
... return a
...
>>> f = Fib()
>>> f[0]
1
>>> f[1]
1
>>> f[10]
89
>>> f[20]
10946
>>> f[30]
1346269

但是list有个神奇的切片方法:

1
2
>>> range(100)[5:10]
[5, 6, 7, 8, 9]

对于Fib却报错。原因是__getitem__()传入的参数可能是一个int,也可能是一个切片对象slice,所以要做判断:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Fib(object):
def __getitem__(self, n):
if isinstance(n, int):
a, b = 1, 1
for x in range(n):
a, b = b, a + b
return a
if isinstance(n, slice):
start = n.start
stop = n.stop
a, b = 1, 1
L = []
for x in range(stop + 1):
if x >= start:
L.append(a)
a, b = b, a + b
return L

getattr

通过__getattr__()方法我们可以返回一个不存在的属性

1
2
3
4
5
6
7
8
>>> class SubClass(object):
... def __getattr__(self,name):
... if name=='age':
... return lambda:22
...
>>> s = SubClass()
>>> s.age()
22

call

设置一个函数,通过实例本身调用

1
2
3
4
5
6
7
8
9
>>> class Student(object):
... def __init__(self,name):
... self.name = name
... def __call__(self):
... print('My name is %s' % self.name)
...
>>> s = Student('zoe')
>>> s()
My name is zoe

通过callable()函数,我们就可以判断一个对象是否是“可调用”对象。

1
2
3
4
5
6
7
8
9
10
>>> callable(max)
True
>>> callable([1,2,4])
False
>>> callable(None)
False
>>> callable('str')
False
>>> callable(Student('zoe'))
True

针对Activity而言,Android存在一个回退栈维护界面体验。当一个应用程序图标被点击时,应用的Task就会来到前台,并把应用的主Activity压入BackStack的栈顶,并获得焦点,这个Activity称之为根Activity,而BackStack中的Activity可以通过点击回退键弹出栈并销毁,使上一个Activity获得焦点,直到用户返回到Home页,而当BackStack中的Activity都被销毁之后,这个Task就不服存在,这个程序的进程还存在(不在此时销毁)。

Task的状态

每个个Task都存在一个BackStack,而系统中可以存在多个Task,但是每次只有一个Task获得前台焦点,一般而言,系统允许用户在多个Task中切换,而被置于后台的Task的Activity将被置于Stopped状态。实际上,同一个Task中的Activity,只要不存在于栈顶并且获得前台焦点,那么它就是一个Stopped的状态。

Activity启动模式

根据Activity的不同启动模式,它在BackStack中的状态时不一样的。Activity可以通过AndroidManifest.xml清单文件配置,在节点中的android:laucherMode属性设置。它有四个选项:

  • standard
  • singleTop
  • singleTask
  • singeInstance

standard

标准启动模式,也是默认缺省值。standard模式下的Activity会依照启动顺序压入BackStack中,示意图如下:

singleTop

单顶模式,这种模式下,启动一个Activity的时候如果发现BackStack的栈顶已经存在这个Activity了,就不会去重建新的Activity,而是复用这个栈顶已经存在的Activity,避免同一个Activity被重复开启。示意图如下:

singleTop应用的场景很多,一般适用于可以复用而又有多个开启渠道的Activity,避免当一个Activity已经开启并获得焦点后,再次重复开启。比如说Android系统浏览器的书签页面。

singleTask

开启一个Activity的时候,检查BackStack里面是否有这个Activity的实例存在,如果存在,清空BackStack里这个Activity上所有的其他Activity。

singleInstance

被标记为singleInstance启动模式的Activity,在启动的时候,会开启一个新的BackStack,这个BackStack里只有一个Activity的实例存在,并且使这个BackStack获得焦点。这是一种极端的模式,它会导致整个设备的操作系统里,只存在一个这个Activity实例,不论从何处启动的。

singleInstance一般只存在一个适用场景,Android系统的来电页面,多次来电均使用的是一个Activity。

当然,在Android中,除了在AndroidManifest.xml清单文件中配置LauncherMode属性外,还可以在代码中设置启动模式。在组件中,启动一个Activity,需要用到startActivity()方法,其中传递一个Intent,可以使用Intent.setFlags(int flags)来设置新启动的Activity的启动模式,而通过代码设置Activity的启动模式的方式,优先级要高于在AndroidManifest.xml清单文件中的配置。

Intent.setFlag(int flags)方法传递的一个整形的数据,被Android系统设置为了常量:

  • FLAG_ACTIVITY_NEW_TASK:这个标识会使新启动的Activity独立创建一个Task。
  • FLAG_ACTIVITY_CLEAR_TOP:这个标识会使新启动的Activity检查是否存在于Task中,如果存在则清除其之上的Activity,使它获得焦点,并不重新实例化一个Activity,一般结合FLAG_ACTIVITY_NEW_TASK一起使用。
  • FLAG_ACTIVITY_SINGLE_TOP:等同于在LauncherMode属性设置为singleTop。

  1. 开启WebView的JS支持
1
webView.getSettings().setJavaScriptEnabled(true);
  1. 对WebView进行JavascriptInterface绑定,JS脚本通过这个借口来调用Java代码
1
2
//	this为借口对象,第二个参数为JS中改参数的别名
webView.addJavascriptInterface(this,"zoe");
  1. Java中调用JS
1
2
// 调用JS的test函数并传参"test"
webView.loadUrl("javascript:test('"+test+"')");
  1. js中调用Java函数
1
<div id='b'><a onclick="window.zoe.onClick">b.c</a></div>

示例

layout

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >

<WebView
android:id="@+id/webview"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="11" />


<Button
android:id="@+id/button1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="调用js无参函数" />
<Button
android:id="@+id/button2"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="调用js有参函数" />

</LinearLayout>

activity

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
public class MainActivity extends Activity {

private WebView contentView = null;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
contentView = (WebView)findViewById(R.id.webview);
contentView.getSettings().setJavaScriptEnabled(true);
contentView.loadUrl("file:///android_asset/web.html");
contentView.addJavascriptInterface(this, "zoe");
Button button1 = (Button)findViewById(R.id.button1);
Button button2 = (Button)findViewById(R.id.button2);
button1.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
contentView.loadUrl("javascript:fun1()");
}
});
button2.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
contentView.loadUrl("javascript:fun2('参数')");
}
});
}
public void startFunction() {
Toast.makeText(this, "js调用了java函数", Toast.LENGTH_SHORT).show();
runOnUiThread(new Runnable() {
@Override
public void run() {

}
});
}
public void startFunction(final String str) {
Toast.makeText(this, "js调用了java函数,并传参:"+str, Toast.LENGTH_SHORT).show();
runOnUiThread(new Runnable() {
@Override
public void run() {

}
});
}
}

web.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
<script type="text/javascript">
function fun1(){
document.getElementById("content").innerHTML +=
"<br\>java调用了js函数fun1";
}

function fun2(arg){
document.getElementById("content").innerHTML +=
("<br\>"+"java调用了js函数fun2,并传参:"+arg);
}

</script>
</head>
<body>
HTML标题 <br/>
<a onClick="window.zoe.startFunction()">调用java函数</a><br/>
<a onClick="window.zoe.startFunction('hello java')">调用java函数并传递参数</a>
<br/>
<div id="content">这里是内容</div>
</body>
</html>


例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<shape>
<!-- 填充 -->
<solid android:color="# ff9d77">
<!-- 渐变 -->
<gradient
android:startColor="# ff8c00"
android:endColor="# FFFFFF"
android:angle="270" />
<!-- 描边 -->
<stroke
android:width="2dp"
android:color="# dcdcdc" />
<!-- 圆角 -->
<corners android:radius="2dp" />
<padding
android:left="10dp"
android:top="10dp"
android:right="10dp"
android:bottom="10dp" />

solid:填充
gradient:渐变
startColor和endColor分别为起始和结束颜色
angle是渐变角度,必须是45的整倍数
type是渐变模式,可选linear,即线性渐变;也可以为radial径向渐变,径向渐变需要制定半径gradientRadius=”50”
strokr:描边
width指宽度,color值颜色,dashWitdh指定虚线宽,dashGap指定相隔距离
corners:原件
radius角的弧度,值越大角越圆

Swift初识

WWDC2014年6月3日苹果开发者大会发布,2010年7约开始开发

  • 基于C语言和Objective-C语言,使用现有的Cocoa和Cocoa Touch框架,无缝兼容C、Objective-C语言
  • 兼具编译语言的高性能(Performance)和脚背语言的交互性(Interactive)
  • 支持Playground,它允许程序实时预览,无需频繁创建和运行App
  • 简洁、安全、容易、灵活、高效

使用let来声明常量,使用var来声明变量。常量只能赋一次值。

值声明的类型必须和赋的值一致,声明时类型是可选的,声明的同时赋值,编译器会自动推断类型。也可以在声明时,指定类型。

1
2
3
var myVariable = 42
let myConstant = 42
let explicitDouble:Double = 70

值永远不会被隐式转换为其他类型,如果需要把一个值转换成其他类型,需要显示转换。

1
2
3
let label = "The width is"
let width = 94
let widthLabel = label + String(width)

有一种更简单的把值转换成字符串的方法:把值写到括号里,并且在括号之前加一个反斜杠。

1
2
let apples = 3
let appleSummary = "I have \(apples) apples."

使用方括号[]来创建数组和字典,并使用下标或者键(key)来访问元素。

1
2
3
4
5
6
7
8
var shoppingList = [catfish", "water", "tulips", "blue paint"]
shoppingList[1] = "bottle of water"

var occupations = [
"Malcolm": "Captain",
"Kaylee": "Mechanic",
]
occupations["Jayne"] = "Public Relations"

创建一个空数组或者字典,可以使用初始化语法

1
2
let emptyArray = String[]()
let emptyDictionary = Dictionary<String,Float>()

如果类型信息可以被推断出来,你可以用[]和[:]来创建数组和空字典————就像声明变量或者给函数传参数的时候一样。

1
shoppingList = []

控制流

使用ifswitch来进行条件操作,使用for-inforwhiledo-while来进行循环,包括条件和循环变量的括号可以省略,但是语句体的大括号是必须的。

if语句中,条件必须是一个布尔表达式

switch支持任意类型的数据及各种比较操作

1
2
3
4
5
6
7
8
9
10
let vegetable = "red pepper"
switch vegetable {
let vegetableComment = "Add some raisins and make ants on a log."
case "cucumber", "watercress":
let vegetableComment = "That would make a good tea sandwich."
case let x where x.hasSuffix("pepper"):
let vegetableComment = "Is it a spicy \(x)?"
default:
let vegetableComment = "Everything tastes good in soup."
}

运行switch中匹配到的子句之后,程序会退出switch语句,并不会继续向下运行,所以不需要在每个子句结尾写break

for-in可以遍历字典

1
2
3
4
5
6
7
8
9
10
11
12
13
14
let interestingNumbers = [
"Prime": [2, 3, 5, 7, 11, 13],
"Fibonacci": [1, 1, 2, 3, 5, 8],
"Square": [1, 4, 9, 16, 25],
]
var largest = 0
for (kind, numbers) in interestingNumbers {
for number in numbers {
if number > largest {
largest = number
}
}
}
largest

使用while来重复运行一段代码直到不满足条件。循环条件可以在开头也可以在结尾。

1
2
3
4
5
6
7
8
9
10
11
var n = 2
while n < 100 {
n = n * 2
}
n

var m = 2
do {
m = m * 2
} while m < 100
m

你可以在循环中使用..来表示范围,也可以使用传统的写法,两者是等价的:

1
2
3
4
5
6
7
8
9
10
11
var firstForLoop = 0
for i in 0..3 {
firstForLoop += i
}
firstForLoop

var secondForLoop = 0
for var i = 0; i < 3; ++i {
secondForLoop += 1
}
secondForLoop

函数和闭包

使用func来声明一个函数,使用名字和参数来调用函数,使用->来指定函数返回值

1
2
3
4
func greet(name:String,day:String) -> String{
return "Hello \(name),today is \(day)."
}
greet("Bob","Tuesday")

使用一个元组来返回多个值

1
2
3
4
func getGasPrices() -> (Double,Double,Double){
return (3.59,3.69,3.79)
}
getGasPrices()

函数可以带有可变个数的参数,这些参数在函数内表现为数组的形式:

1
2
3
4
5
6
7
8
func sumOf(number:Int...) -> Int {
var sum = 0
for number in numbers {
sum += number
}
return sum
}
sumOf(42,597,12)

函数可以嵌套,被嵌套的函数可以访问外侧函数的变量

1
2
3
4
5
6
7
8
9
func returnFifteen() -> Int {
var y = 10
func add() {
y += 5
}
add()
return y
}
returnFifteen()

函数也可以作为另一个函数的返回值
func makeIncrementer() -> (Int-> Int){
func addOne(number: Int) -> Int {
return 1 + number
}
return addOne
}
var increment = makeIncrementer()
increment(7)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
函数也可以当做参数传入另一个函数
```swift
func hasAnyMatches(list:Int[],condition:Int -> Bool) -> Bool{
for item in list {
if condition(item){
return true
}
}
return false;
}
func lessThanTen(number: Int) -> Bool {
return number < 10
}
var numbers = [20,19,7,12]
hasAnyMatches(numbers,lessThanTen)

函数实际上是一种特殊的闭包,可以使用{}来创建一个匿名闭包,使用in来将参数和返回值类型声明与闭包函数体进行分离

1
2
3
4
5
numbers.map({
(number:Int) -> Int in
let result = 3 * number
return result
})

如果一个闭包的类型已知,比如作为一个回调函数,可以忽略参数的类型和返回值,单个语句闭包会把它语句的值当作结果返回

1
number.map({number in 3 *number})

可以通过参数位置而不是参数名字来引用参数————这种方法在非常短的闭包中很有用,当一个闭包作为最后一个参数传给一个函数的时候,它可以直接跟在括号后面

1
2
3
4
5
6
7
8
9
10
11
sort([1,5,3,12,2]){$0 > $1}

## 对象和类
使用`class`和类名来创建一个类,类中属性的声明和常量变量声明一样,唯一区别的是上下文是类,同样方法和函数声明也一样
```swift
class Shape{
var numberOfSides = 0
func simpleDescriotion() -> String{
return "A shape with \(numberOfSides) sides."
}
}

创建一个类的实例,在类名后加上括号,使用点语法来访问实例的属性和方法

1
2
3
var shape = Shape()
shape.numberOfSides = 7
var shapeDescription = shape.simpleDescription()

使用init来创建一个构造器

1
2
3
4
5
6
7
8
9
10
11
12
class NamedShape{
vae numberOfSides:Int = 0
var name:String

init(name:String) {
self.name = name
}

func simpleDescription() -> String {
return "A shape with \(numberOfSides) sides."
}
}

如果你需要在删除对象之前进行一些清理工作,使用deinit创建一个析构函数。

子类的定义方法是在类名后加上父类的名字,用冒号分割,创建类的时候并不需要一个标准的根类,可以忽略父类

子类如果重写父类的方法的话,需要用override标记————如果没有添加override就重写父类方法的话编译器会报错,编译器同样会检测override标记的方法是否确实在父类中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Square:NamsedShape {
var sideLength:Double

init(sideLength:Double,name:String){
self.sideLength = sideLength
super.init(name: name)
numberOfSides = 4
}
func area() -> Double {
return sideLength * sideLength
}

override func simpleDescription() -> String {
return "A square with sides of length \(sideLength)."
}
}
let test = Square(sideLength: 5.2, name: "my test square")
test.area()
test.simpleDescription()

属性可以有 getter 和 setter 。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class EquilateralTriangle: NamedShape {
var sideLength: Double = 0.0

init(sideLength: Double, name: String) {
self.sideLength = sideLength
super.init(name: name)
numberOfSides = 3
}

var perimeter: Double {
get {
return 3.0 * sideLength
}
set {
sideLength = newValue / 3.0
}
}

override func simpleDescription() -> String {
return "An equilateral triagle with sides of length \(sideLength)."
}
}
var triangle = EquilateralTriangle(sideLength: 3.1, name: "a triangle")
triangle.perimeter
triangle.perimeter = 9.9
triangle.sideLength

perimeter的setter中,新值的名字是newValue。也可以再set之后显示的设置一个名字

不需要计算属性,但是仍然需要在设置一个新值之前或者之后运行代码,使用willSetdidSet

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 确保三角形的边长总是和正方形的边长相同
class TriangleAndSquare {
var triangle : EquilateralTriangle {
willSet{
square.sideLength = newValue.sideLength
}
}
var square:Square {
willSet {
triangle.sideLength = newValue.sideLength
}
}
init(size:Double,name:String){
square = Square(sideLength:size,name:name)
triangle = EquilateralTriangle(sideLength:size,name:name)
}
}
var triangeleAndSquare = TriangleAndSquare(size:10,name:"another test shape")
triangleAndSquare.square.sideLength
triangleAndSquare.triangle.sideLenth
triangleAndSquare.square = Square(sideLength:50,name:"larger square")
triangleAndSquare.triangle.sideLength

类中的方法和一般的函数有一个重要的区别,函数的参数名只在函数内部使用,但是方法的参数名需要在调用的时候显示说明(除了第一个参数)。默认情况下,方法的参数名和它在方法内部的名字一样,不过也可以定义第二个名字,这个名字被用在方法内部

1
2
3
4
5
6
7
8
class Counter {
var count:Int = 0
func incrementBy(amout:Int,numberOfTimers times:Int){
count += amount * times
}
}
var counter = Counter()
counter.incrementyBy(2,numberOfTimes:8)

出来变量的可选值时,可以再操作(比如方法、属性和子脚本)之前加?,如果?之前的值是nil,?后面的东西都会被忽略,并且整个表达式返回nil,否则,?之后的东西都会被运行,这两种情况下,整个表示式的值也是一个可选值

1
2
let optionalSquare:Square ?= Square(sideLength:2.5,name:"optional square")
let sideLength = optionalSquare?.sideLength

枚举和结构体

使用enum来创建一个枚举,枚举可以包含方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
enum Rank:Int {
case Ace = 1
case Two,Three,Four,Five,Six,Seven,Eight,Nine,Ten
case Jack,Queen,King
func simpleDesription() -> String {
switch self{
case .Ace:
return "ace"
case .Jack:
return "Jack"
case .Queen:
return "Queen"
case .King:
return "King"
default:
return String(self.toRaw())
}
}
}
let ace = Rank.Ace
let aceRawValue = ace.toRaw()

上面的枚举原始值的类型是Int,所以需要设置当一个原始值,剩下的原始值会按照顺序赋值。也可以使用字符串或者浮点数作为枚举的原始值。

使用toRawfromRaw函数来在原始值和枚举值之间进行转换

1
2
3
if let convertedRank = Rank.froRaw(3) {
let threeDescription = convertedRank.simpleDescription()
}

枚举的成员是实际值,并不是原始值的另一种表达式。如果原始值没有意义,不重要设置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
enum Suit {
case Spades,Hearts,Diamonds,Clubs
func simpleDescription() -> String {
switch self {
case .Spades:
return "spades"
case .Hearts:
return "hearts"
case .Diamonds:
return "diamonds"
case .Clubs
return "clubs"
}
}
}
let hearts = Suit.Hearts
let heartsDescription = hearts.simpleDescription()

使用struct来创建一个结构体,结构体和类有很多相同的地方,比如方法和构造器,他们之间最大的区别就是,结构体是值传递,类是引用传递

1
2
3
4
5
6
7
8
9
struct Card{
var rank:Rank
var suit:Suit
func simpleDescription() -> String {
return "The \(rank.simpleDescription()) of \(suit.simpleDescription)"
}
}
let threeOfSpades = Card(rank: .Three,suit: .Spades)
let threeOfSpadesDescription = threeOfSpades.simleDescription()

协议和扩展

使用protocol来声明一个协议

1
2
3
4
protocol ExampleProtocol {
var simpleDescription: String{get}
mutating func adjust()
}

类、枚举和结构体都可以实现协议

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class SimpleClass:ExampleProtocol {
var simpleDescription:String = "A very simple class"
var anotherProperty: Int = 69105
func adjust(){
simpleDescription += "Noew 100% adjusted."
}
}
var a = SimpleClass()
a.adjust()
let aDescription = a.simpleDescription

struct SimpleStructire:ExampleProtocol{
var simpleDescription:String = "A simple structure"
mutating func adjust(){
simpleDescription += "(adjust)"
}
}
var b = SimpleStructire()
b.adjust()
let bDescruption = b.simpleDescription

声明的时候mutating关键字用来标记一个会修改结构体的方法。

使用extension来为现有的类型添加功能,比如新的方法和参数。

1
2
3
4
5
6
7
8
9
extension Int: ExampleProtocol{
var simpleDescription:String{
return "The number \(self)"
}
mutating func adjust(){
self += 42
}
}
7.simpleDescription

你可以像使用其他命名类型一样使用协议名——例如,创建一个有不同类型但是都实现一个协议的对象集合。当你处理类型是协议的值时,协议外定义的方法不可用。

1
2
3
let protocolValue: ExampleProtocol = a
protocolValue.simpleDescription
// protocolValue.anotherProperty // Uncomment to see the error

泛型

在尖括号里写一个名字来创建一个泛型函数或者类型

1
2
3
4
5
6
7
8
func repeat<ItemType>(item: ItemType,times:Int) -> ItemType[]{
var result = ItemType[]()
for i in 0..times{
result += item
}
return result
}
repeat("knock",4)

也可以创建泛型类、枚举和结构体

1
2
3
4
5
6
enum OptionalValue<T> {
case None
case Some(T)
}
var possibleInteger: OptionalValue<Int> = .None
possibleInteger = .Some(100)

在类型名后面使用where来指定类型的需求,比如,限定类型实现某一个协议,限定两个类型是相同的,或者限定某个类必须有一个特定的父类

1
2
3
4
5
6
7
8
9
10
11
func anyCommonElements <T,U where T:Sequence,U:Sequence,T.GeneratorType.Element:Equatable,T.GeneratorType.Element == U.GeneratorType.Element> (1hs: T, rhs: U) -> Bool {
or lhsItem in lhs {
for rhsItem in rhs {
if lhsItem == rhsItem {
return true
}
}
}
return false
}
anyCommonElements([1, 2, 3], [3])

简单起见,可以忽略where,只在冒号后面写协议或者类名。<T: Equatable>和是等价的。

在Python中,一个.py文件就称为一个模块(Module)。
为了避免模块名冲突,Python按目录来组织模块,称之为包(backage)。
每个包目录下面都会有一个__init__py文件,这个文件是必须存在的,否则,Python就把这个目录当成普通目录,而不是一个包。__init__.py可以使空文件,也可以有Python代码。

使用模块

Python本身就内置了很多非常有用的模块,只要安装完毕,这些模块就可以立刻使用。
sys模块为例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# !/usr/bin/env python
# -*- coding:utf-8 -*-

' a test module '
# 第一个字符串会被默认视为模块的文档说明
__author__ = 'Zoe'

import sys

def test():
args = sys.argv
if len(args) == 1:
print 'Hello,world!'
elif len(args) == 2:
print 'Hello,%s!' % args[1]
else:
print 'Too many arguments!'

if __name__=='__main__':
test()
# 当命令行运行本模块时,Python解释器吧一个特殊变量__name__置为__main__,而如果在其他地方导入该模块,if判断将会失败。
# 要导入后调用才会生效if判断

别名

导入模块,还可以使用别名,这样,可以在运行时根据当前环境选择最合适的模块。
比如Python标准库一般会提供StringIO和cStringIO两个库,这两个库的接口和功能是一样的,但是cStringIO是C写的,速度更快,所以,你会经常看到这样的写法:

1
2
3
4
try:
import cStringIO as StringIO
except ImportError: # 导入失败会捕获到ImportError
import StringIO

还有类似simplejson这样的库,在Python 2.6之前是独立的第三方库,从2.6开始内置,所以,会有这样的写法:

1
try: import json #  python >= 2.6 except ImportError: import simplejson as json #  python <= 2.5

作用域

在一个模块中,我们可能会定义很多函数和变量,但有的函数和变量我们不希望别人使用,有的函数和变量我们希望仅仅在模块内部使用,在Python中,是通过_前缀来实现的。
正常的函数和变量名是公开的(public),可以被直接饮用。
类似__xxx__这样的变量是特殊变量,可以被直接饮用,但是有特殊用途,比如__name____author__,模块定义的文档注释课可以用__doc__访问。
类似_xxx__xxx这样的函数或变量就是非公开的(private),不应该被直接饮用。

安装第三方模块

首先需要安装setuptools工具,Mac和Linux自带了此工具,Windows
下载get-pip文件,运行Python get-pip.py命令,并将\Python27\Spripts路径添加到Path
安装第三方模块easy_install XXX即可
安装一个第三方库——Python Imaging Library,这是Python下非常强大的处理图像的工具库。一般来说,第三方库都会在Python官方的pypi.python.org网站注册,要安装一个第三方库,必须先知道该库的名称,可以在官网或者pypi上搜索,比如Python Imaging Library的名称叫PIL,因此,安装Python Imaging Library的命令就是:

1
easy_install PIL

模块搜索路径

当加载一个模块时,Python会在指定的路径下搜索对应的.py文件(搜索当前目录、所有已安装的内置模块和第三方模块),如果找不到会报错。
要添加自己的搜索目录,一是直接修改模块的目录,即修改.path变量,而是设置在环境变量中。

使用__future__

Python提供了__future__模块,把下一个新版本的特性导入到当前版本,于是我们就可以在当前版本中测试一些新版本的特性。举例说明如下:

为了适应Python 3.x的新的字符串的表示方法,在2.7版本的代码中,可以通过unicode_literals来使用Python 3.x的新的语法:

1
2
3
4
5
6
7
8
#  still running on Python 2.7

from __future__ import unicode_literals

print '\'xxx\' is unicode?', isinstance('xxx', unicode)
print 'u\'xxx\' is unicode?', isinstance(u'xxx', unicode)
print '\'xxx\' is str?', isinstance('xxx', str)
print 'b\'xxx\' is str?', isinstance(b'xxx', str)

函数是一种把大段代码封装的一种形式,通过一层层的函数调用,可以把复杂的任务分解成简单的任务,这种分解可以称之为面向过程的程序设计。函数就是面向过程的程序设计的基本单元。
函数式编程——Functional Programming可以归结到面向过程的程序设计,但其思想更接近数学计算。
函数式编程就是一种抽象程度很高的编程范式,纯粹的函数式编程编写的函数没有变量。因此任意一个函数只要输入是确定的,输出就是确定的,这种函数我们称之为没有副作用。而允许使用变量的程序设计语言,由于韩式内部的变量状态不稳定,同样的输入,可能得到不同的输出,因此这种函数就是有副作用的。
函数式编程的一个特点就是允许把函数本身作为参数传入另一个函数,还允许返回一个函数!
Python对函数式编程提供部分支持,由于Python允许使用变量,因此,Python不是纯函数式编程语言。

高阶函数

传入函数

map()函数接收两个参数,一个函数,一个是序列,map将传入函数一次作用到序列的每个元素,并把结果作为新的list返回。
举例说明,比如我们有一个函数f(x)=x^2,要把这个函数作用在一个list [1, 2, 3, 4, 5, 6, 7, 8, 9]上,就可以用map()实现如下:

1
2
3
4
5
>>> def f(x):
... return x * x
...
>>> map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9])
[1, 4, 9, 16, 25, 36, 49, 64, 81]

map()函数这种能够接收函数作为参数的函数,称之为高阶函数Higher-order function)。

再看reduce的用法。reduce把一个函数作用在一个序列[x1, x2, x3…]上,这个函数必须接收两个参数,reduce把结果继续和序列的下一个元素做累积计算,其效果就是:

1
reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)

比方说对一个序列求和,就可以用reduce实现:

1
2
3
4
5
>>> def add(x, y):
... return x + y
...
>>> reduce(add, [1, 3, 5, 7, 9])
25

考虑到字符串str也是一个序列,对上面的例子稍加改动,配合map(),我们就可以写出把str转换为int的函数:

1
2
3
4
5
>>> def fn(x, y):
... return x * 10 + y
...
>>> reduce(fn, map(int, '13579'))
13579

整理成一个str2int的函数就是:

1
2
3
4
def str2int(s):
def fn(x, y):
return x * 10 + y
return reduce(fn, map(int, s))

还可以用lambda函数进一步简化成:

1
2
def str2int(s):
return reduce(lambda x,y: x*10+y, map(int, s))

排序算法

Python内置的sorted()函数可以对list进行排序

1
2
>>> sorted([34,12,43,65,1])
[1, 12, 34, 43, 65]

sorted()函数也是一个高阶函数,它还可以接收一个比较函数来实现自定义的排序,比如实现一个倒叙排序

1
2
3
4
5
6
def reversed_cmp(x,y):
if x > y:
return -1
if x < y:
return 1
return 0

传入自定义的比较函数reversed_cmp,就可以实现倒序排序:

1
2
>>> sorted([36, 5, 12, 9, 21], reversed_cmp)
[36, 21, 12, 9, 5]

再看一个字符串排序的例子:

1
2
>>> sorted(['about', 'bob', 'Zoo', 'Credit'])
['Credit', 'Zoo', 'about', 'bob']

默认情况下,对字符串排序,是按照ASCII的大小比较的,由于’Z’ < ‘a’,结果,大写字母Z会排在小写字母a的前面。

函数作为返回值

高阶函数除了可以接收函数作为参数外,也可以把函数作为结果值返回。

1
2
3
4
5
6
7
def lazy_sum(*args):
def sum():
ax = 0
for n in args:
ax = ax + n
return ax
return sum

调用lazy_sum()时,返回的并不是求和结果,而是求和函数:

1
2
3
4
>>> f = lazy_sum(1, 3, 5, 7, 9)
>>> f
<function sum at 0x10452f668>
>>> f() # 调用函数f时,才真正计算求和的结果

匿名函数

当我们在传入函数时,有时候,不需要显式地定义函数,直接传入匿名函数更方便。
在Python中,对匿名函数提供了有限支持。还是以map()函数为例,计算f(x)=x2时,除了定义一个f(x)的函数外,还可以直接传入匿名函数:

1
2
>>> map(lambda x: x * x, [1, 2, 3, 4, 5, 6, 7, 8, 9])
[1, 4, 9, 16, 25, 36, 49, 64, 81]

通过对比可以看出,匿名函数lambda x: x * x实际上就是:

1
2
def f(x):
return x * x

关键字lambda表示匿名函数,冒号前面的x表示函数参数。

匿名函数有个限制,就是只能有一个表达式,不用写return,返回值就是该表达式的结果。

用匿名函数有个好处,因为函数没有名字,不必担心函数名冲突。此外,匿名函数也是一个函数对象,也可以把匿名函数赋值给一个变量,再利用变量来调用该函数:

1
2
3
4
5
>>> f = lambda x: x * x
>>> f
<function <lambda> at 0x10453d7d0>
>>> f(5)
25

同样,也可以把匿名函数作为返回值返回,比如:

1
2
def build(x, y):
return lambda: x * x + y * y

装饰器

由于函数也是一个对象,而且函数对象可以被赋值给变量,所以通过变量也能调用该函数

1
2
3
4
5
6
>>> def now():
... print '2014-03-16'
...
>>> f = now
>>> f()
2014-03-16

函数对象有一个__name__属性,可以拿到函数的名字:

1
2
3
4
>>> now.__name__
'now'
>>> f.__name__
'now'

假设我们要增强now()函数的功能,比如,在函数调用前后自动打印日志,但又不希望修改now()函数的定义,这种在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator)。
本质上,decorator就是一个返回函数的高阶函数。所以,我们要定义一个能打印日志的decorator,可以定义如下:

1
2
3
4
5
def log(func):
def wrapper(*args, **kw):
print 'call %s():' % func.__name__
return func(*args, **kw)
return wrapper

观察上面的log,因为它是一个decorator,所以接受一个函数作为参数,并返回一个函数。我们要借助Python的@语法,把decorator置于函数的定义处:

1
2
3
@log
def now():
print '2013-12-25'

调用now()函数,不仅会运行now()函数本身,还会在运行now()函数前打印一行日志:

1
2
3
>>> now()
call now():
2013-12-25

把@log放到now()函数的定义处,相当于执行了语句:

1
now = log(now)

由于log()是一个decorator,返回一个函数,所以,原来的now()函数仍然存在,只是现在同名的now变量指向了新的函数,于是调用now()将执行新函数,即在log()函数中返回的wrapper()函数。
wrapper()函数的参数定义是(*args, **kw),因此,wrapper()函数可以接受任意参数的调用。在wrapper()函数内,首先打印日志,再紧接着调用原始函数。

如果decorator本身需要传入参数,那就需要编写一个返回decorator的高阶函数,写出来会更复杂。比如,要自定义log的文本:

1
2
3
4
5
6
7
def log(text):
def decorator(func):
def wrapper(*args, **kw):
print '%s %s():' % (text, func.__name__)
return func(*args, **kw)
return wrapper
return decorator

这个3层嵌套的decorator用法如下:

1
2
3
@log('execute')
def now():
print '2013-12-25'

执行结果如下:

1
2
3
>>> now()
execute now():
2013-12-25

和两层嵌套的decorator相比,3层嵌套的效果是这样的:

1
>>> now = log('execute')(now)

我们来剖析上面的语句,首先执行log(‘execute’),返回的是decorator函数,再调用返回的函数,参数是now函数,返回值最终是wrapper函数。

以上两种decorator的定义都没有问题,但还差最后一步。因为我们讲了函数也是对象,它有__name__等属性,但你去看经过decorator装饰之后的函数,它们的__name__已经从原来的’now’变成了’wrapper’:

1
2
>>> now.__name__
'wrapper'

因为返回的那个wrapper()函数名字就是’wrapper’,所以,需要把原始函数的__name__等属性复制到wrapper()函数中,否则,有些依赖函数签名的代码执行就会出错。

不需要编写wrapper.__name__ = func.__name__这样的代码,Python内置的functools.wraps就是干这个事的,所以,一个完整的decorator的写法如下:

1
2
3
4
5
6
7
8
import functools

def log(func):
@functools.wraps(func)
def wrapper(*args, **kw):
print 'call %s():' % func.__name__
return func(*args, **kw)
return wrapper

或者针对带参数的decorator:

1
2
3
4
5
6
7
8
9
10
import functools

def log(text):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kw):
print '%s %s():' % (text, func.__name__)
return func(*args, **kw)
return wrapper
return decorator

import functools是导入functools模块。模块的概念稍候讲解。现在,只需记住在定义wrapper()的前面加上@functools.wraps(func)即可。

偏函数

Python的functools模块提供了很多有用的功能,其中一个就是偏函数(Parial function)
functools.partial的作用就是,把一个函数的某些参数(不管有没有默认值)给固定住(也就是设置默认值),返回一个新的函数,调用这个新函数会更简单。

1
2
3
4
5
6
7
>>> import functools
>>> def fun(i):
... print i
...
>>> fun2 = functools.partial(fun,i = 1)
>>> fun2()
1

SwipRefreshLayout是Google官方推出的一款下拉刷新组件,可以实现Google Now上的下拉刷新效果。
SwipRefreshLayout只能有一个直接子View,且该组件必须是支持下拉刷新的,比如ListView和ScrollView

Demo示例

activity_main.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >

<android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/swipe_refresh"
android:layout_width="match_parent"
android:layout_height="match_parent" >

<ListView
android:id="@+id/listview"
android:layout_width="match_parent"
android:layout_height="match_parent" >
</ListView>
</android.support.v4.widget.SwipeRefreshLayout>

</LinearLayout>

listview_item.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content" >

<TextView
android:id="@+id/item_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="15dp"
android:gravity="center"
android:singleLine="true"
android:textSize="16sp"
android:textStyle="bold" />
</RelativeLayout>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
public class MainActivity extends Activity implements SwipeRefreshLayout.OnRefreshListener{
private SwipeRefreshLayout swipeLayout;
private ListView listView;
private ListViewAdapter adapter;
private List<ItemInfo> infoList;

@Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

swipeLayout = (SwipeRefreshLayout)this.findViewById(R.id.swipe_refresh);
swipeLayout.setOnRefreshListener(this);

swipeLayout.setColorScheme(android.R.color.holo_red_light,android.R.color.holo_green_light,android.R.color.holo_blue_bright,android.R.color.orange_light);

infoList = new ArrayList<ItemInfo>();
Item info = new ItemInfo();
info.setName("icon");
infoList.add(info);
listView = (ListView) findViewById(R.id.listView);
adapter = new ListViewAdapter(this,infoList);

}

public void onRefresh(){
new Handler().postDelayed(new Runnable(){
public void run(){
ItemInfo info = new ItemInfo();
info.setName("icon-refresh");
info.add(info);
adapter.notifyDataSetChanged();
swipeLayout.setRefreshing(false);
}
},1500);
}
}
class ItemInfo {

/**
* id
*/
private int id;

/**
* name
*/
private String name;

public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

}
class ListViewAdapter extends ArrayAdapter<ItemInfo> {

private LayoutInflater inflater;

public ListViewAdapter(Context context, List<ItemInfo> list) {
super(context, 0, list);
inflater = LayoutInflater.from(context);
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
ItemInfo info = getItem(position);

if (convertView == null) {
convertView = inflater.inflate(R.layout.item_listview, null);
}

TextView name = (TextView) convertView.findViewById(R.id.item_name);
name.setText(info.getName());

return convertView;
}

}